From 0b6761acdb77ca66e0bd41bc530d4d791df2078d Mon Sep 17 00:00:00 2001 From: pavish Date: Mon, 12 Aug 2024 23:12:48 +0530 Subject: [PATCH 01/36] Move database page contents into schema section within db page --- mathesar/urls.py | 2 +- mathesar_ui/src/App.svelte | 18 ++-- .../src/components/AppSecondaryHeader.svelte | 13 +-- .../src/components/PageTitleAndMeta.svelte | 17 ++-- mathesar_ui/src/i18n/languages/en/dict.json | 4 + .../src/pages/database/DatabasePage.svelte | 84 +++++++++++++++-- ...seDetails.svelte => SchemasSection.svelte} | 94 +------------------ .../src/pages/database/SettingsSection.svelte | 0 .../src/pages/schema/SchemaPage.svelte | 2 - mathesar_ui/src/routes/AdminRoute.svelte | 1 - mathesar_ui/src/routes/DatabaseRoute.svelte | 17 +++- mathesar_ui/src/routes/urls.ts | 10 +- 12 files changed, 123 insertions(+), 139 deletions(-) rename mathesar_ui/src/pages/database/{DatabaseDetails.svelte => SchemasSection.svelte} (64%) create mode 100644 mathesar_ui/src/pages/database/SettingsSection.svelte diff --git a/mathesar/urls.py b/mathesar/urls.py index 1daba441f7..3a8d1d7c4c 100644 --- a/mathesar/urls.py +++ b/mathesar/urls.py @@ -60,7 +60,7 @@ path('db//', views.schemas, name='schemas'), path('i18n/', include('django.conf.urls.i18n')), re_path( - r'^db/(?P\w+)/(?P\w+)/', + r'^db/(?P\w+)/schemas/(?P\w+)/', views.schemas_home, name='schema_home' ), diff --git a/mathesar_ui/src/App.svelte b/mathesar_ui/src/App.svelte index a0b1812e01..d4317163e0 100644 --- a/mathesar_ui/src/App.svelte +++ b/mathesar_ui/src/App.svelte @@ -56,15 +56,15 @@ --color-text-muted: #6b7280; --color-substring-match: rgb(254, 221, 72); --color-substring-match-light: rgba(254, 221, 72, 0.2); - --text-size-xx-small: var(--size-xx-small); // 8px - --text-size-x-small: var(--size-x-small); // 10px - --text-size-small: var(--size-small); // 12px - --text-size-base: var(--size-base); // 14px - --text-size-large: var(--size-large); // 16px - --text-size-x-large: var(--size-x-large); // 18px - --text-size-xx-large: var(--size-xx-large); // 20px - --text-size-ultra-large: var(--size-ultra-large); // 24px - --text-size-super-ultra-large: var(--size-super-ultra-large); // 32px + --text-size-xx-small: var(--size-xx-small); + --text-size-x-small: var(--size-x-small); + --text-size-small: var(--size-small); + --text-size-base: var(--size-base); + --text-size-large: var(--size-large); + --text-size-x-large: var(--size-x-large); + --text-size-xx-large: var(--size-xx-large); + --text-size-ultra-large: var(--size-ultra-large); + --text-size-super-ultra-large: var(--size-super-ultra-large); --modal-z-index: 50; --modal-record-selector-z-index: 50; diff --git a/mathesar_ui/src/components/AppSecondaryHeader.svelte b/mathesar_ui/src/components/AppSecondaryHeader.svelte index 72d242b98d..602ab86476 100644 --- a/mathesar_ui/src/components/AppSecondaryHeader.svelte +++ b/mathesar_ui/src/components/AppSecondaryHeader.svelte @@ -5,14 +5,9 @@ export let pageTitleAndMetaProps: ComponentProps; export let restrictWidth = true; - export let theme: 'dark' | 'light' | undefined = undefined; -
+
@@ -24,11 +19,7 @@
diff --git a/mathesar_ui/src/i18n/languages/en/dict.json b/mathesar_ui/src/i18n/languages/en/dict.json index ffc63e4e3d..7843238c83 100644 --- a/mathesar_ui/src/i18n/languages/en/dict.json +++ b/mathesar_ui/src/i18n/languages/en/dict.json @@ -131,6 +131,7 @@ "database": "Database", "database_name": "Database Name", "database_not_found": "Database with id [connectionId] is not found.", + "database_permissions": "Database Permissions", "database_server_credentials": "Database server credentials", "database_type": "Database Type", "databases": "Databases", @@ -148,6 +149,7 @@ "delete_connection_db_delete_info": "If you would like to delete the database too, you will need to do so from outside Mathesar by deleting it directly in PostgreSQL.", "delete_connection_info": "The database will not be deleted and will still be accessible outside Mathesar. You may choose to reconnect to it in the future; however, upon disconnecting you will lose Mathesar-specific metadata such as saved explorations, customized column display options, and customized record summary templates.", "delete_connection_with_name": "Delete [connectionName] Database Connection?", + "delete_database": "Delete Database", "delete_exploration": "Delete Exploration", "delete_import": "Delete Import", "delete_item": "Delete {item}", @@ -168,6 +170,7 @@ "disallow_null_values_help": "Enable this option to prevent null values in the column. Null values are empty values that are not the same as zero or an empty string.", "discard_changes": "Discard Changes", "disconnect": "Disconnect", + "disconnect_database": "Disconnect Database", "display_language": "Display Language", "display_name": "Display Name", "documentation_and_resources": "Documentation & Resources", @@ -470,6 +473,7 @@ "select_user": "Select User", "set_constraint_name": "Set Constraint Name", "set_to": "Set to", + "settings": "Settings", "setup_connections_help": "Seems you haven't set up any databases. To use Mathesar, you'll need to connect one.", "share": "Share", "share_exploration": "Share Exploration", diff --git a/mathesar_ui/src/pages/database/DatabasePage.svelte b/mathesar_ui/src/pages/database/DatabasePage.svelte index b4f2d16aff..37ad749074 100644 --- a/mathesar_ui/src/pages/database/DatabasePage.svelte +++ b/mathesar_ui/src/pages/database/DatabasePage.svelte @@ -1,11 +1,49 @@ @@ -13,14 +51,46 @@ - {#key database.id} - - {/key} + +
+ + + + + {$_('disconnect_database')} + + + {$_('delete_database')} + + +
+
+ + + {#if activeTab?.id === 'schemas'} + + {:else if activeTab?.id === 'settings'} + + {/if} +
diff --git a/mathesar_ui/src/pages/database/DatabaseDetails.svelte b/mathesar_ui/src/pages/database/SchemasSection.svelte similarity index 64% rename from mathesar_ui/src/pages/database/DatabaseDetails.svelte rename to mathesar_ui/src/pages/database/SchemasSection.svelte index 8008fa11a0..0f50b468ac 100644 --- a/mathesar_ui/src/pages/database/DatabaseDetails.svelte +++ b/mathesar_ui/src/pages/database/SchemasSection.svelte @@ -1,21 +1,12 @@ - - -
- - -
- {$_('sync_external_changes')} - -

- {$_('sync_external_changes_structure_help')} -

-

- {$_('sync_external_changes_data_help')} -

-
-
-
- {#if userProfile?.isSuperUser} - editConnectionModal.open()} - > - {$_('edit_connection')} - - deleteConnectionModal.open()} - > - {$_('delete_connection')} - - {/if} -
-
-
-
-

{$_('schemas')} ({schemasMap.size})

diff --git a/mathesar_ui/src/pages/database/SettingsSection.svelte b/mathesar_ui/src/pages/database/SettingsSection.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/mathesar_ui/src/pages/schema/SchemaPage.svelte b/mathesar_ui/src/pages/schema/SchemaPage.svelte index 06f233263c..7340f7c5f2 100644 --- a/mathesar_ui/src/pages/schema/SchemaPage.svelte +++ b/mathesar_ui/src/pages/schema/SchemaPage.svelte @@ -91,10 +91,8 @@ > diff --git a/mathesar_ui/src/routes/AdminRoute.svelte b/mathesar_ui/src/routes/AdminRoute.svelte index ed9ad6a965..48cad96d58 100644 --- a/mathesar_ui/src/routes/AdminRoute.svelte +++ b/mathesar_ui/src/routes/AdminRoute.svelte @@ -34,7 +34,6 @@ > - - - + + + + + - + Date: Tue, 13 Aug 2024 00:50:07 +0530 Subject: [PATCH 02/36] Implement Database model class --- mathesar_ui/src/api/rpc/configured_roles.ts | 20 +++++------ mathesar_ui/src/api/rpc/database_setup.ts | 22 ++++++------ mathesar_ui/src/api/rpc/databases.ts | 36 +++---------------- mathesar_ui/src/api/rpc/servers.ts | 4 +-- .../src/components/DatabaseName.svelte | 2 +- .../breadcrumb/DatabaseSelector.svelte | 2 +- .../breadcrumb/EntitySelector.svelte | 2 +- .../breadcrumb/SchemaSelector.svelte | 2 +- .../components/breadcrumb/breadcrumbTypes.ts | 2 +- mathesar_ui/src/models/databases.ts | 20 +++++++++++ mathesar_ui/src/models/servers.ts | 15 ++++++++ .../data-explorer/DataExplorerPage.svelte | 2 +- .../pages/database/AddEditSchemaModal.svelte | 2 +- .../src/pages/database/DatabasePage.svelte | 4 +-- .../src/pages/database/SchemaRow.svelte | 2 +- .../src/pages/database/SchemasSection.svelte | 2 +- .../pages/exploration/ExplorationPage.svelte | 2 +- .../src/pages/exploration/Header.svelte | 2 +- mathesar_ui/src/pages/home/DatabaseRow.svelte | 4 +-- mathesar_ui/src/pages/home/HomePage.svelte | 2 +- .../preview/ImportPreviewContent.svelte | 2 +- .../import/preview/ImportPreviewPage.svelte | 2 +- .../import/preview/importPreviewPageUtils.ts | 2 +- .../import/upload/ImportUploadPage.svelte | 2 +- .../schema/CreateEmptyTableButton.svelte | 2 +- .../CreateNewExplorationTutorial.svelte | 2 +- .../pages/schema/CreateNewTableButton.svelte | 2 +- .../schema/CreateNewTableTutorial.svelte | 2 +- .../src/pages/schema/ExplorationItem.svelte | 2 +- .../src/pages/schema/ExplorationsList.svelte | 2 +- .../pages/schema/SchemaExplorations.svelte | 2 +- .../src/pages/schema/SchemaOverview.svelte | 2 +- .../src/pages/schema/SchemaPage.svelte | 2 +- .../src/pages/schema/SchemaTables.svelte | 2 +- mathesar_ui/src/pages/schema/TableCard.svelte | 2 +- .../src/pages/schema/TablesList.svelte | 2 +- .../src/routes/DataExplorerRoute.svelte | 2 +- mathesar_ui/src/routes/DatabaseRoute.svelte | 2 +- .../src/routes/ExplorationRoute.svelte | 2 +- mathesar_ui/src/routes/ImportRoute.svelte | 2 +- mathesar_ui/src/routes/RecordPageRoute.svelte | 2 +- mathesar_ui/src/routes/SchemaRoute.svelte | 2 +- mathesar_ui/src/routes/TableRoute.svelte | 2 +- .../src/stores/abstract-types/store.ts | 2 +- mathesar_ui/src/stores/databases.ts | 19 +++++++--- mathesar_ui/src/stores/schemas.ts | 2 +- .../src/stores/table-data/TableStructure.ts | 2 +- mathesar_ui/src/stores/table-data/columns.ts | 2 +- .../src/stores/table-data/constraints.ts | 2 +- mathesar_ui/src/stores/table-data/records.ts | 2 +- .../src/stores/table-data/tabularData.ts | 2 +- mathesar_ui/src/stores/tables.ts | 2 +- .../ConnectDatabaseModal.svelte | 2 +- .../ConnectExistingDatabase.svelte | 2 +- .../create-database/CreateNewDatabase.svelte | 2 +- mathesar_ui/src/utils/preloadData.ts | 10 +++--- 56 files changed, 132 insertions(+), 114 deletions(-) create mode 100644 mathesar_ui/src/models/databases.ts create mode 100644 mathesar_ui/src/models/servers.ts diff --git a/mathesar_ui/src/api/rpc/configured_roles.ts b/mathesar_ui/src/api/rpc/configured_roles.ts index 351452b2f6..bb30bae9ec 100644 --- a/mathesar_ui/src/api/rpc/configured_roles.ts +++ b/mathesar_ui/src/api/rpc/configured_roles.ts @@ -1,10 +1,10 @@ import { rpcMethodTypeContainer } from '@mathesar/packages/json-rpc-client-builder'; -import type { Server } from './servers'; +import type { RawServer } from './servers'; -export interface ConfiguredRole { +export interface RawConfiguredRole { id: number; - server_id: Server['id']; + server_id: RawServer['id']; name: string; } @@ -12,30 +12,30 @@ export interface ConfiguredRole { export const configured_roles = { list: rpcMethodTypeContainer< { - server_id: ConfiguredRole['server_id']; + server_id: RawConfiguredRole['server_id']; }, - Array + Array >(), add: rpcMethodTypeContainer< { - server_id: ConfiguredRole['server_id']; - name: ConfiguredRole['name']; + server_id: RawConfiguredRole['server_id']; + name: RawConfiguredRole['name']; password: string; }, - ConfiguredRole + RawConfiguredRole >(), delete: rpcMethodTypeContainer< { - configured_role_id: ConfiguredRole['id']; + configured_role_id: RawConfiguredRole['id']; }, void >(), set_password: rpcMethodTypeContainer< { - configured_role_id: ConfiguredRole['id']; + configured_role_id: RawConfiguredRole['id']; password: string; }, void diff --git a/mathesar_ui/src/api/rpc/database_setup.ts b/mathesar_ui/src/api/rpc/database_setup.ts index 41f42ef584..81c5a0f93b 100644 --- a/mathesar_ui/src/api/rpc/database_setup.ts +++ b/mathesar_ui/src/api/rpc/database_setup.ts @@ -1,8 +1,8 @@ import { rpcMethodTypeContainer } from '@mathesar/packages/json-rpc-client-builder'; -import type { ConfiguredRole } from './configured_roles'; -import type { DatabaseResponse } from './databases'; -import type { Server } from './servers'; +import type { RawConfiguredRole } from './configured_roles'; +import type { RawDatabase } from './databases'; +import type { RawServer } from './servers'; export const sampleDataOptions = [ 'library_management', @@ -12,16 +12,16 @@ export const sampleDataOptions = [ export type SampleDataSchemaIdentifier = (typeof sampleDataOptions)[number]; export interface DatabaseConnectionResult { - server: Server; - database: DatabaseResponse; - configured_role: ConfiguredRole; + server: RawServer; + database: RawDatabase; + configured_role: RawConfiguredRole; } // eslint-disable-next-line @typescript-eslint/naming-convention export const database_setup = { create_new: rpcMethodTypeContainer< { - database: DatabaseResponse['name']; + database: RawDatabase['name']; sample_data?: SampleDataSchemaIdentifier[]; }, DatabaseConnectionResult @@ -29,10 +29,10 @@ export const database_setup = { connect_existing: rpcMethodTypeContainer< { - host: Server['host']; - port: Server['port']; - database: DatabaseResponse['name']; - role: ConfiguredRole['name']; + host: RawServer['host']; + port: RawServer['port']; + database: RawDatabase['name']; + role: RawConfiguredRole['name']; password: string; sample_data?: SampleDataSchemaIdentifier[]; }, diff --git a/mathesar_ui/src/api/rpc/databases.ts b/mathesar_ui/src/api/rpc/databases.ts index 1fa2b32e37..10e9a11628 100644 --- a/mathesar_ui/src/api/rpc/databases.ts +++ b/mathesar_ui/src/api/rpc/databases.ts @@ -1,44 +1,18 @@ import { rpcMethodTypeContainer } from '@mathesar/packages/json-rpc-client-builder'; -import type { Server } from './servers'; +import type { RawServer } from './servers'; -export interface DatabaseResponse { +export interface RawDatabase { id: number; name: string; - server_id: Server['id']; -} - -/** - * TODO_BETA: Modify after store discussion is resolved - */ -export class Database implements DatabaseResponse { - readonly id: number; - - readonly name: string; - - readonly server_id: number; - - readonly server_host: string; - - readonly server_port: number; - - constructor(databaseResponse: DatabaseResponse, server: Server) { - this.id = databaseResponse.id; - this.name = databaseResponse.name; - if (databaseResponse.server_id !== server.id) { - throw new Error('Server ids do not match'); - } - this.server_id = databaseResponse.server_id; - this.server_host = server.host; - this.server_port = server.port; - } + server_id: RawServer['id']; } export const databases = { list: rpcMethodTypeContainer< { - server_id?: DatabaseResponse['server_id']; + server_id?: RawDatabase['server_id']; }, - Array + Array >(), }; diff --git a/mathesar_ui/src/api/rpc/servers.ts b/mathesar_ui/src/api/rpc/servers.ts index eaa9faec31..f92a29bebb 100644 --- a/mathesar_ui/src/api/rpc/servers.ts +++ b/mathesar_ui/src/api/rpc/servers.ts @@ -1,11 +1,11 @@ import { rpcMethodTypeContainer } from '@mathesar/packages/json-rpc-client-builder'; -export interface Server { +export interface RawServer { id: number; host: string; port: number; } export const servers = { - list: rpcMethodTypeContainer>(), + list: rpcMethodTypeContainer>(), }; diff --git a/mathesar_ui/src/components/DatabaseName.svelte b/mathesar_ui/src/components/DatabaseName.svelte index f7855f97eb..186344afc2 100644 --- a/mathesar_ui/src/components/DatabaseName.svelte +++ b/mathesar_ui/src/components/DatabaseName.svelte @@ -1,6 +1,6 @@ diff --git a/mathesar_ui/src/pages/schema/SchemaPage.svelte b/mathesar_ui/src/pages/schema/SchemaPage.svelte index 7159d8d095..31e3c9adee 100644 --- a/mathesar_ui/src/pages/schema/SchemaPage.svelte +++ b/mathesar_ui/src/pages/schema/SchemaPage.svelte @@ -15,11 +15,10 @@ import { modal } from '@mathesar/stores/modal'; import { queries } from '@mathesar/stores/queries'; import { currentTablesData as tablesStore } from '@mathesar/stores/tables'; + import AddEditSchemaModal from '@mathesar/systems/schemas/AddEditSchemaModal.svelte'; import { logEvent } from '@mathesar/utils/telemetry'; import { Button, Icon, TabContainer } from '@mathesar-component-library'; - import AddEditSchemaModal from '../database/AddEditSchemaModal.svelte'; - import ExplorationSkeleton from './ExplorationSkeleton.svelte'; import SchemaExplorations from './SchemaExplorations.svelte'; import SchemaOverview from './SchemaOverview.svelte'; diff --git a/mathesar_ui/src/pages/database/AddEditSchemaModal.svelte b/mathesar_ui/src/systems/schemas/AddEditSchemaModal.svelte similarity index 97% rename from mathesar_ui/src/pages/database/AddEditSchemaModal.svelte rename to mathesar_ui/src/systems/schemas/AddEditSchemaModal.svelte index a1bc8d8cbe..c96c8faf39 100644 --- a/mathesar_ui/src/pages/database/AddEditSchemaModal.svelte +++ b/mathesar_ui/src/systems/schemas/AddEditSchemaModal.svelte @@ -1,4 +1,3 @@ - - + diff --git a/mathesar_ui/src/components/routing/MultiPathRoute.svelte b/mathesar_ui/src/components/routing/MultiPathRoute.svelte index 7be61b5539..abc4ecbe59 100644 --- a/mathesar_ui/src/components/routing/MultiPathRoute.svelte +++ b/mathesar_ui/src/components/routing/MultiPathRoute.svelte @@ -37,8 +37,8 @@ {#each paths as rp (rp.name)} setPath(rp, e.detail)} - on:unload={() => clearPath(rp)} + onLoad={(meta) => setPath(rp, meta)} + onUnload={() => clearPath(rp)} firstmatch /> {/each} diff --git a/mathesar_ui/src/components/routing/RouteObserver.svelte b/mathesar_ui/src/components/routing/RouteObserver.svelte index 105f5c5df8..55f214cb28 100644 --- a/mathesar_ui/src/components/routing/RouteObserver.svelte +++ b/mathesar_ui/src/components/routing/RouteObserver.svelte @@ -1,33 +1,21 @@ From 8133181e670734991fe7382e6dc24295536274f1 Mon Sep 17 00:00:00 2001 From: pavish Date: Thu, 15 Aug 2024 21:48:38 +0530 Subject: [PATCH 06/36] Add routes for all sections in database page --- mathesar/urls.py | 6 +- mathesar_ui/src/i18n/languages/en/dict.json | 5 ++ mathesar_ui/src/models/databases.ts | 5 +- ...Page.svelte => DatabasePageWrapper.svelte} | 21 ++--- .../database/settings/Collaborators.svelte | 5 ++ .../settings/RoleConfiguration.svelte | 5 ++ .../src/pages/database/settings/Roles.svelte | 5 ++ .../database/settings/SettingsSection.svelte | 3 - .../database/settings/SettingsWrapper.svelte | 77 +++++++++++++++++++ mathesar_ui/src/routes/DatabaseRoute.svelte | 61 +++++++++++---- mathesar_ui/src/routes/urls.ts | 12 +++ 11 files changed, 170 insertions(+), 35 deletions(-) rename mathesar_ui/src/pages/database/{DatabasePage.svelte => DatabasePageWrapper.svelte} (81%) create mode 100644 mathesar_ui/src/pages/database/settings/Collaborators.svelte create mode 100644 mathesar_ui/src/pages/database/settings/RoleConfiguration.svelte create mode 100644 mathesar_ui/src/pages/database/settings/Roles.svelte delete mode 100644 mathesar_ui/src/pages/database/settings/SettingsSection.svelte create mode 100644 mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte diff --git a/mathesar/urls.py b/mathesar/urls.py index 3a8d1d7c4c..004d216c42 100644 --- a/mathesar/urls.py +++ b/mathesar/urls.py @@ -57,11 +57,15 @@ path('shares/tables//', views.shared_table, name='shared_table'), path('shares/explorations//', views.shared_query, name='shared_query'), path('databases/', views.databases, name='databases'), - path('db//', views.schemas, name='schemas'), path('i18n/', include('django.conf.urls.i18n')), re_path( r'^db/(?P\w+)/schemas/(?P\w+)/', views.schemas_home, name='schema_home' ), + re_path( + r'^db/(?P\w+)/((schemas|settings)/)?', + views.schemas, + name='schemas' + ), ] diff --git a/mathesar_ui/src/i18n/languages/en/dict.json b/mathesar_ui/src/i18n/languages/en/dict.json index 7843238c83..0733249cf7 100644 --- a/mathesar_ui/src/i18n/languages/en/dict.json +++ b/mathesar_ui/src/i18n/languages/en/dict.json @@ -51,6 +51,7 @@ "cleaning_up": "Cleaning up", "clear": "Clear", "clear_value": "Clear value", + "collaborators": "Collaborators", "column": "Column", "column_added_number_of_times": "{count, plural, one {This column has been added once.} other {This column has been added {count} times.}}", "column_data_types": "Column Data Types", @@ -251,6 +252,7 @@ "if_upgrade_succeeds_help": "If the upgrade succeeds, you will see that you're running the latest version.", "import": "Import", "import_from_file": "Import from a File", + "in_mathesar": "In Mathesar", "in_this_table": "In this table", "individual_permissions_admin_modify_warning": "Individual permissions cannot be modified for users with Admin access.", "inherited": "Inherited", @@ -358,6 +360,7 @@ "number_of_matches_in_category": "{count, plural, one {{count} match for [searchValue] in [categoryName]} other {{count} matches for [searchValue] in [categoryName]}}", "old_password": "Old Password", "oldest_to_newest_sort": "Oldest-Newest", + "on_the_server": "On The Server", "one_column_from_base_is_required": "At least one column from the base table is required to add columns from linked tables.", "one_to_many": "One to Many", "one_to_many_link_desc": "One [baseTable] record can be linked from multiple [targetTable] records.", @@ -431,6 +434,8 @@ "retry": "Retry", "reuse_credentials_from_known_connection": "Reuse credentials from a known connection", "role": "Role", + "role_configuration": "Role Configuration", + "roles": "Roles", "row": "Row", "running_latest_version": "You are running the latest version", "sample_data_library_help": "Sample data from a fictional library.", diff --git a/mathesar_ui/src/models/databases.ts b/mathesar_ui/src/models/databases.ts index 96b50c3f1e..4d56f06833 100644 --- a/mathesar_ui/src/models/databases.ts +++ b/mathesar_ui/src/models/databases.ts @@ -5,16 +5,13 @@ import type { Server } from './servers'; export class Database { readonly id: number; - name: string; + readonly name: string; readonly server: Server; constructor(props: { server: Server; rawDatabase: RawDatabase }) { this.id = props.rawDatabase.id; this.name = props.rawDatabase.name; - if (props.rawDatabase.server_id !== props.server.id) { - throw new Error('Server ids do not match'); - } this.server = props.server; } } diff --git a/mathesar_ui/src/pages/database/DatabasePage.svelte b/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte similarity index 81% rename from mathesar_ui/src/pages/database/DatabasePage.svelte rename to mathesar_ui/src/pages/database/DatabasePageWrapper.svelte index e286b3f829..2e82d9ffb3 100644 --- a/mathesar_ui/src/pages/database/DatabasePage.svelte +++ b/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte @@ -21,11 +21,10 @@ TabContainer, } from '@mathesar-component-library'; - import SchemasSection from './schemas/SchemasSection.svelte'; - import SettingsSection from './settings/SettingsSection.svelte'; - export let database: Database; - export let section: string; + + type Section = 'schemas' | 'settings'; + let section: Section = 'schemas'; $: tabs = [ { @@ -39,10 +38,10 @@ href: getDatabasePageSettingsSectionUrl(database.id), }, ]; - $: activeTab = tabs.find((tab) => tab.id === section) ?? tabs[0]; + $: activeTab = tabs.find((tab) => tab.id === section); - function openPermissionsModal() { - // + export function setSection(_section: Section) { + section = _section; } @@ -65,7 +64,7 @@ }} >
- @@ -88,11 +87,7 @@
- {#if activeTab?.id === 'schemas'} - - {:else if activeTab?.id === 'settings'} - - {/if} +
diff --git a/mathesar_ui/src/pages/database/settings/Collaborators.svelte b/mathesar_ui/src/pages/database/settings/Collaborators.svelte new file mode 100644 index 0000000000..5c75a672eb --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/Collaborators.svelte @@ -0,0 +1,5 @@ + + +{$_('collaborators')} diff --git a/mathesar_ui/src/pages/database/settings/RoleConfiguration.svelte b/mathesar_ui/src/pages/database/settings/RoleConfiguration.svelte new file mode 100644 index 0000000000..23aa8257fd --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/RoleConfiguration.svelte @@ -0,0 +1,5 @@ + + +{$_('role_configuration')} diff --git a/mathesar_ui/src/pages/database/settings/Roles.svelte b/mathesar_ui/src/pages/database/settings/Roles.svelte new file mode 100644 index 0000000000..3636bb90da --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/Roles.svelte @@ -0,0 +1,5 @@ + + +{$_('roles')} diff --git a/mathesar_ui/src/pages/database/settings/SettingsSection.svelte b/mathesar_ui/src/pages/database/settings/SettingsSection.svelte deleted file mode 100644 index 220a4ef6f0..0000000000 --- a/mathesar_ui/src/pages/database/settings/SettingsSection.svelte +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte b/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte new file mode 100644 index 0000000000..320be93c8f --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte @@ -0,0 +1,77 @@ + + + + +
+ +
+
+ + diff --git a/mathesar_ui/src/routes/DatabaseRoute.svelte b/mathesar_ui/src/routes/DatabaseRoute.svelte index f4ade64fbe..5ffacc0b19 100644 --- a/mathesar_ui/src/routes/DatabaseRoute.svelte +++ b/mathesar_ui/src/routes/DatabaseRoute.svelte @@ -6,9 +6,14 @@ import AppendBreadcrumb from '@mathesar/components/breadcrumb/AppendBreadcrumb.svelte'; import Identifier from '@mathesar/components/Identifier.svelte'; import { RichText } from '@mathesar/components/rich-text'; - import MultiPathRoute from '@mathesar/components/routing/MultiPathRoute.svelte'; + import EventfulRoute from '@mathesar/components/routing/EventfulRoute.svelte'; import type { Database } from '@mathesar/models/databases'; - import DatabasePage from '@mathesar/pages/database/DatabasePage.svelte'; + import DatabasePageWrapper from '@mathesar/pages/database/DatabasePageWrapper.svelte'; + import DatabasePageSchemasSection from '@mathesar/pages/database/schemas/SchemasSection.svelte'; + import DatabaseCollaborators from '@mathesar/pages/database/settings/Collaborators.svelte'; + import DatabaseRoleConfiguration from '@mathesar/pages/database/settings/RoleConfiguration.svelte'; + import DatabaseRoles from '@mathesar/pages/database/settings/Roles.svelte'; + import DatabasePageSettingsWrapper from '@mathesar/pages/database/settings/SettingsWrapper.svelte'; import ErrorPage from '@mathesar/pages/ErrorPage.svelte'; import { databasesStore } from '@mathesar/stores/databases'; @@ -29,24 +34,52 @@ {#if $currentDatabase} - - - - - - + + + + + + setSection('schemas')}> + + + setSection('settings')} + firstmatch + > + + + setSettingsSection('roleConfiguration')} + > + + + setSettingsSection('collaborators')} + > + + + setSettingsSection('roles')} + > + + + + + + {:else} diff --git a/mathesar_ui/src/routes/urls.ts b/mathesar_ui/src/routes/urls.ts index bd7bbfbab0..879c3874ce 100644 --- a/mathesar_ui/src/routes/urls.ts +++ b/mathesar_ui/src/routes/urls.ts @@ -10,6 +10,18 @@ export function getDatabasePageSettingsSectionUrl(databaseId: number): string { return `/db/${databaseId}/settings/`; } +export function getDatabaseRoleConfigurationUrl(databaseId: number): string { + return `${getDatabasePageSettingsSectionUrl(databaseId)}role-configuration/`; +} + +export function getDatabaseCollaboratorsUrl(databaseId: number): string { + return `${getDatabasePageSettingsSectionUrl(databaseId)}collaborators/`; +} + +export function getDatabaseRolesUrl(databaseId: number): string { + return `${getDatabasePageSettingsSectionUrl(databaseId)}roles/`; +} + export function getSchemaPageUrl(databaseId: number, schemaId: number): string { return `${getDatabasePageSchemasSectionUrl(databaseId)}${schemaId}/`; } From a2981e03fb0b2bba22e6e1450c68ad5f2abba186 Mon Sep 17 00:00:00 2001 From: pavish Date: Fri, 16 Aug 2024 13:53:47 +0530 Subject: [PATCH 07/36] Implement batching api requests --- .../json-rpc-client-builder/requests.ts | 80 +++++++++++++++---- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/mathesar_ui/src/packages/json-rpc-client-builder/requests.ts b/mathesar_ui/src/packages/json-rpc-client-builder/requests.ts index 0ad5cfd337..c25af1f163 100644 --- a/mathesar_ui/src/packages/json-rpc-client-builder/requests.ts +++ b/mathesar_ui/src/packages/json-rpc-client-builder/requests.ts @@ -26,6 +26,15 @@ function cancellableFetch( ); } +function getRpcRequestBody(request: RpcRequest, id = 0) { + return { + jsonrpc, + id, + method: request.method, + params: request.params, + }; +} + function makeRpcResponse(value: unknown): RpcResponse { if (hasProperty(value, 'result')) { const response: RpcResult = { @@ -44,12 +53,7 @@ function send(request: RpcRequest): CancellablePromise> { ...request.getHeaders(), 'Content-Type': 'application/json', }, - body: JSON.stringify({ - jsonrpc, - id: 0, - method: request.method, - params: request.params, - }), + body: JSON.stringify(getRpcRequestBody(request)), }); return new CancellablePromise( (resolve) => @@ -67,6 +71,47 @@ function send(request: RpcRequest): CancellablePromise> { ); } +function makeRpcBatchResponse[]>( + values: unknown, +): RpcBatchResponse { + if (!Array.isArray(values)) { + throw new Error('Response is not an array'); + } + return values.map((value) => makeRpcResponse(value)) as RpcBatchResponse; +} + +function sendBatchRequest[]>( + endpoint: string, + headers: Record, + requests: T, +): CancellablePromise> { + const fetch = cancellableFetch(endpoint, { + method: 'POST', + headers: { + ...headers, + 'Content-Type': 'application/json', + }, + body: JSON.stringify( + requests.map((request, index) => getRpcRequestBody(request, index)), + ), + }); + return new CancellablePromise( + (resolve) => + void fetch + .then( + (response) => response.json(), + (rejectionReason) => + resolve( + requests.map(() => + RpcError.fromAnything(rejectionReason), + ) as RpcBatchResponse, + ), + ) + .then((json) => resolve(makeRpcBatchResponse(json))), + () => fetch.cancel(), + ); +} + export type GetHeaders = () => Record; export class RpcRequest { @@ -126,16 +171,23 @@ export class RpcRequest { } } -export type RpcBatchResponse[]> = - CancellablePromise<{ - [K in keyof T]: T[K] extends RpcRequest ? RpcResponse : never; - }>; +export type RpcBatchResponse[]> = { + [K in keyof T]: T[K] extends RpcRequest ? RpcResponse : never; +}; export function batchSend[]>( - ...requests: T -): RpcBatchResponse { - // TODO implement batch sending - throw new Error('Not implemented'); + requests: T, +): CancellablePromise> { + if (requests.length === 0) { + throw new Error('There must be atleast one request'); + } + const [firstRequest, ...rest] = requests; + const { endpoint } = firstRequest; + if (rest.some((request) => request.endpoint !== endpoint)) { + throw new Error('Only RPC requests to the same endpoint can be batched'); + } + // TODO: Decide if headers need to be merged + return sendBatchRequest(endpoint, firstRequest.getHeaders(), requests); } /** From 805c6d0e2ee439ba809405226f7f00b0fdf73cc7 Mon Sep 17 00:00:00 2001 From: pavish Date: Fri, 16 Aug 2024 13:54:14 +0530 Subject: [PATCH 08/36] Implement AsyncRpcApiStore --- mathesar_ui/src/stores/AsyncRpcApiStore.ts | 69 ++++++++++++++++++++++ mathesar_ui/src/stores/AsyncStore.ts | 44 ++++++++++++-- 2 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 mathesar_ui/src/stores/AsyncRpcApiStore.ts diff --git a/mathesar_ui/src/stores/AsyncRpcApiStore.ts b/mathesar_ui/src/stores/AsyncRpcApiStore.ts new file mode 100644 index 0000000000..68c1057bdc --- /dev/null +++ b/mathesar_ui/src/stores/AsyncRpcApiStore.ts @@ -0,0 +1,69 @@ +import { CancellablePromise } from '@mathesar/component-library'; +import { + type RpcRequest, + type RpcResponse, + batchSend, +} from '@mathesar/packages/json-rpc-client-builder'; + +import AsyncStore from './AsyncStore'; + +export default class AsyncRpcApiStore extends AsyncStore< + Props, + U +> { + apiRpcFn: (props: Props) => RpcRequest; + + postProcess: (response: T) => U; + + constructor( + rpcFn: (props: Props) => RpcRequest, + options?: Partial<{ + getError: (caughtValue: unknown) => string; + initialValue: U; + postProcess: (response: T) => U; + }>, + ) { + const postProcess = + options?.postProcess ?? ((response: T) => response as unknown as U); + super( + (props: Props) => + new CancellablePromise((resolve, reject) => { + rpcFn(props) + .run() + .then( + (value) => resolve(postProcess(value)), + (error) => reject(error), + ) + .catch((error) => reject(error)); + }), + ); + this.apiRpcFn = rpcFn; + this.postProcess = postProcess; + } + + batchRunner( + props: Props, + ): [RpcRequest, (response: RpcResponse) => void] { + const onReponse = (response: RpcResponse) => { + if (response.status === 'ok') { + this.setResolvedValue(this.postProcess(response.value)); + } else { + this.setRejectedError(response); + } + }; + return [this.apiRpcFn(props), onReponse]; + } + + static async runBatched( + batchRunners: [ + RpcRequest, + (response: RpcResponse) => void, + ][], + ) { + const requests = batchRunners.map((runner) => runner[0]); + const results = await batchSend(requests); + batchRunners.forEach((runner, index) => { + runner[1](results[index]); + }); + } +} diff --git a/mathesar_ui/src/stores/AsyncStore.ts b/mathesar_ui/src/stores/AsyncStore.ts index a21be3ae1d..33830f3d0e 100644 --- a/mathesar_ui/src/stores/AsyncStore.ts +++ b/mathesar_ui/src/stores/AsyncStore.ts @@ -10,7 +10,10 @@ import { } from 'svelte/store'; import { getErrorMessage } from '@mathesar/utils/errors'; -import type { CancellablePromise } from '@mathesar-component-library'; +import { + type CancellablePromise, + hasProperty, +} from '@mathesar-component-library'; export type AsyncStoreSettlement = | { state: 'resolved'; value: T } @@ -111,19 +114,30 @@ export default class AsyncStore constructor( run: (props: Props) => Promise | CancellablePromise, - getError: (caughtValue: unknown) => string = getErrorMessage, + options?: Partial<{ + getError: (caughtValue: unknown) => string; + initialValue: T; + }>, ) { this.runFn = run; - this.getError = getError; + this.getError = options?.getError ?? getErrorMessage; + if (hasProperty(options, 'initialValue')) { + this.value = writable( + new AsyncStoreValue({ + isLoading: false, + settlement: { state: 'resolved', value: options.initialValue as T }, + }), + ); + } } subscribe( - run: Subscriber>, + subscriber: Subscriber>, invalidate?: | ((value?: AsyncStoreValue | undefined) => void) | undefined, ): Unsubscriber { - return this.value.subscribe(run, invalidate); + return this.value.subscribe(subscriber, invalidate); } async run(props: Props): Promise> { @@ -165,6 +179,26 @@ export default class AsyncStore this.cancel(); this.value.set(new AsyncStoreValue({ isLoading: false })); } + + setResolvedValue(value: T) { + this.cancel(); + this.value.set( + new AsyncStoreValue({ + isLoading: false, + settlement: { state: 'resolved', value }, + }), + ); + } + + setRejectedError(error: unknown) { + this.cancel(); + this.value.set( + new AsyncStoreValue({ + settlement: { state: 'rejected', error: this.getError(error) }, + isLoading: false, + }), + ); + } } /* eslint-enable max-classes-per-file */ From 52abcdb4c9f966fba0f6cf9ec1f28aebabd125b5 Mon Sep 17 00:00:00 2001 From: pavish Date: Fri, 16 Aug 2024 14:57:58 +0530 Subject: [PATCH 09/36] Name model class files appropriately --- mathesar_ui/src/components/DatabaseName.svelte | 2 +- mathesar_ui/src/components/breadcrumb/DatabaseSelector.svelte | 2 +- mathesar_ui/src/components/breadcrumb/EntitySelector.svelte | 2 +- mathesar_ui/src/components/breadcrumb/SchemaSelector.svelte | 2 +- mathesar_ui/src/components/breadcrumb/breadcrumbTypes.ts | 2 +- mathesar_ui/src/models/{databases.ts => Database.ts} | 2 +- mathesar_ui/src/models/{servers.ts => Server.ts} | 0 mathesar_ui/src/pages/data-explorer/DataExplorerPage.svelte | 2 +- mathesar_ui/src/pages/database/DatabasePageWrapper.svelte | 2 +- mathesar_ui/src/pages/database/schemas/SchemaRow.svelte | 2 +- mathesar_ui/src/pages/database/schemas/SchemasSection.svelte | 2 +- .../src/pages/database/settings/SettingsWrapper.svelte | 2 +- mathesar_ui/src/pages/exploration/ExplorationPage.svelte | 2 +- mathesar_ui/src/pages/exploration/Header.svelte | 2 +- mathesar_ui/src/pages/home/DatabaseRow.svelte | 2 +- mathesar_ui/src/pages/home/HomePage.svelte | 2 +- .../src/pages/import/preview/ImportPreviewContent.svelte | 2 +- mathesar_ui/src/pages/import/preview/ImportPreviewPage.svelte | 2 +- .../src/pages/import/preview/importPreviewPageUtils.ts | 2 +- mathesar_ui/src/pages/import/upload/ImportUploadPage.svelte | 2 +- mathesar_ui/src/pages/schema/CreateEmptyTableButton.svelte | 2 +- .../src/pages/schema/CreateNewExplorationTutorial.svelte | 2 +- mathesar_ui/src/pages/schema/CreateNewTableButton.svelte | 2 +- mathesar_ui/src/pages/schema/CreateNewTableTutorial.svelte | 2 +- mathesar_ui/src/pages/schema/ExplorationItem.svelte | 2 +- mathesar_ui/src/pages/schema/ExplorationsList.svelte | 2 +- mathesar_ui/src/pages/schema/SchemaExplorations.svelte | 2 +- mathesar_ui/src/pages/schema/SchemaOverview.svelte | 2 +- mathesar_ui/src/pages/schema/SchemaPage.svelte | 2 +- mathesar_ui/src/pages/schema/SchemaTables.svelte | 2 +- mathesar_ui/src/pages/schema/TableCard.svelte | 2 +- mathesar_ui/src/pages/schema/TablesList.svelte | 2 +- mathesar_ui/src/routes/DataExplorerRoute.svelte | 2 +- mathesar_ui/src/routes/DatabaseRoute.svelte | 2 +- mathesar_ui/src/routes/ExplorationRoute.svelte | 2 +- mathesar_ui/src/routes/ImportRoute.svelte | 2 +- mathesar_ui/src/routes/RecordPageRoute.svelte | 2 +- mathesar_ui/src/routes/SchemaRoute.svelte | 2 +- mathesar_ui/src/routes/TableRoute.svelte | 2 +- mathesar_ui/src/stores/abstract-types/store.ts | 2 +- mathesar_ui/src/stores/databases.ts | 4 ++-- mathesar_ui/src/stores/schemas.ts | 2 +- mathesar_ui/src/stores/table-data/TableStructure.ts | 2 +- mathesar_ui/src/stores/table-data/columns.ts | 2 +- mathesar_ui/src/stores/table-data/constraints.ts | 2 +- mathesar_ui/src/stores/table-data/records.ts | 2 +- mathesar_ui/src/stores/table-data/tabularData.ts | 2 +- mathesar_ui/src/stores/tables.ts | 2 +- .../databases/create-database/ConnectDatabaseModal.svelte | 2 +- .../databases/create-database/ConnectExistingDatabase.svelte | 2 +- .../databases/create-database/CreateNewDatabase.svelte | 2 +- mathesar_ui/src/systems/schemas/AddEditSchemaModal.svelte | 2 +- 52 files changed, 52 insertions(+), 52 deletions(-) rename mathesar_ui/src/models/{databases.ts => Database.ts} (89%) rename mathesar_ui/src/models/{servers.ts => Server.ts} (100%) diff --git a/mathesar_ui/src/components/DatabaseName.svelte b/mathesar_ui/src/components/DatabaseName.svelte index 186344afc2..3876453dce 100644 --- a/mathesar_ui/src/components/DatabaseName.svelte +++ b/mathesar_ui/src/components/DatabaseName.svelte @@ -1,6 +1,6 @@ + +
+ +
+ + From c49cf79b432d89667e5a9e36ec3c2a8d16069124 Mon Sep 17 00:00:00 2001 From: pavish Date: Fri, 16 Aug 2024 23:27:57 +0530 Subject: [PATCH 15/36] Add database page settings content layout component --- .../settings/SettingsContentLayout.svelte | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 mathesar_ui/src/pages/database/settings/SettingsContentLayout.svelte diff --git a/mathesar_ui/src/pages/database/settings/SettingsContentLayout.svelte b/mathesar_ui/src/pages/database/settings/SettingsContentLayout.svelte new file mode 100644 index 0000000000..a9bc582a92 --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/SettingsContentLayout.svelte @@ -0,0 +1,34 @@ +
+
+
+ +
+
+ +
+
+
+ +
+
+ + From df5495afc1938cff8940737cd78a77ace4e49bb5 Mon Sep 17 00:00:00 2001 From: pavish Date: Fri, 16 Aug 2024 23:28:15 +0530 Subject: [PATCH 16/36] Add methods to fetch configured roles and roles from database model --- mathesar_ui/src/models/Database.ts | 31 ++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/mathesar_ui/src/models/Database.ts b/mathesar_ui/src/models/Database.ts index f9c3093975..c8b1771553 100644 --- a/mathesar_ui/src/models/Database.ts +++ b/mathesar_ui/src/models/Database.ts @@ -1,5 +1,10 @@ +import { api } from '@mathesar/api/rpc'; import type { RawDatabase } from '@mathesar/api/rpc/databases'; +import AsyncRpcApiStore from '@mathesar/stores/AsyncRpcApiStore'; +import { SortedImmutableMap } from '@mathesar-component-library'; +import { ConfiguredRole } from './ConfiguredRole'; +import { Role } from './Role'; import type { Server } from './Server'; export class Database { @@ -14,4 +19,30 @@ export class Database { this.name = props.rawDatabase.name; this.server = props.server; } + + fetchConfiguredRoles() { + return new AsyncRpcApiStore(api.configured_roles.list, { + postProcess: (rawConfiguredRoles) => + new SortedImmutableMap( + (v) => [...v].sort(([, a], [, b]) => a.name.localeCompare(b.name)), + rawConfiguredRoles.map((rawConfiguredRole) => [ + rawConfiguredRole.id, + new ConfiguredRole({ database: this, rawConfiguredRole }), + ]), + ), + }); + } + + fetchRoles() { + return new AsyncRpcApiStore(api.roles.list, { + postProcess: (rawRoles) => + new SortedImmutableMap( + (v) => [...v].sort(([, a], [, b]) => a.name.localeCompare(b.name)), + rawRoles.map((rawRole) => [ + rawRole.oid, + new Role({ database: this, rawRole }), + ]), + ), + }); + } } From 8e79ac0f1fa7441b748ca26554b567c00fc2a1f0 Mon Sep 17 00:00:00 2001 From: pavish Date: Fri, 16 Aug 2024 23:31:39 +0530 Subject: [PATCH 17/36] Implement database settings context and basic RoleConfiguration page --- .../common/styles/variables.scss | 1 + mathesar_ui/src/i18n/languages/en/dict.json | 8 ++ .../pages/database/DatabasePageWrapper.svelte | 1 + .../settings/RoleConfiguration.svelte | 72 +++++++++++++++++- .../database/settings/SettingsWrapper.svelte | 4 + .../settings/databaseSettingsUtils.ts | 76 +++++++++++++++++++ 6 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts diff --git a/mathesar_ui/src/component-library/common/styles/variables.scss b/mathesar_ui/src/component-library/common/styles/variables.scss index 4e31fd79b5..48e55adc2d 100644 --- a/mathesar_ui/src/component-library/common/styles/variables.scss +++ b/mathesar_ui/src/component-library/common/styles/variables.scss @@ -34,6 +34,7 @@ --slate-700: #424952; --slate-800: #25292e; + --sand-50: #fcfbf8; --sand-100: #f9f8f6; --sand-200: #efece7; --sand-300: #e2dcd4; diff --git a/mathesar_ui/src/i18n/languages/en/dict.json b/mathesar_ui/src/i18n/languages/en/dict.json index 0733249cf7..3e5c6117be 100644 --- a/mathesar_ui/src/i18n/languages/en/dict.json +++ b/mathesar_ui/src/i18n/languages/en/dict.json @@ -45,6 +45,7 @@ "cell": "Cell", "change_password": "Change Password", "check_for_updates": "Check for Updates", + "child_roles": "Child Roles", "choose_database": "Choose a Database", "choose_schema": "Choose a Schema", "choose_table_or_exploration": "Choose a Table or Exploration", @@ -75,6 +76,8 @@ "columns_removed_from_table_added_to_target": "{count, plural, one {The column above will be removed from [tableName] and added to [targetTableName]} other {The columns above will be removed from [tableName] and added to [targetTableName]}}", "columns_to_extract": "Columns to Extract", "columns_to_move": "Columns to Move", + "configure_in_mathesar": "Configure in Mathesar", + "configure_password": "Configure Password", "confirm_and_create_table": "Confirm & create table", "confirm_delete_table": "To confirm the deletion of the [tableName] table, please enter the table name into the input field below.", "confirm_password": "Confirm Password", @@ -114,6 +117,7 @@ "create_new_pg_user": "Create a new PostgreSQL user", "create_new_schema": "Create New Schema", "create_record_from_search": "Create Record From Search Criteria", + "create_role": "Create Role", "create_schema": "Create Schema", "create_share_explorations_of_your_data": "Create and Share Explorations of Your Data", "create_table_move_columns": "Create Table and Move Columns", @@ -175,6 +179,7 @@ "display_language": "Display Language", "display_name": "Display Name", "documentation_and_resources": "Documentation & Resources", + "drop_role": "Drop Role", "edit": "Edit", "edit_connection": "Edit Connection", "edit_connection_with_name": "Edit Connection: [connectionName]", @@ -333,6 +338,7 @@ "new_user_name": "New user name", "new_version_available": "New Version Available", "newest_to_oldest_sort": "Newest-Oldest", + "no": "No", "no_actions_selected_record": "There are no actions to perform on the selected record(s).", "no_constraints": "No Constraints", "no_continue_without_summarization": "No, continue without summarizing", @@ -421,6 +427,7 @@ "release_notes": "Release Notes", "release_notes_and_upgrade_instructions": "Release Notes and Upgrade Instructions", "released_date": "Released {date}", + "remove": "Remove", "remove_filters": "{count, plural, one {Remove Filter} other {Remove {count} Filters}}", "remove_grouping": "Remove Grouping", "remove_old_link_create_new": "Remove old link and create a new link?", @@ -603,6 +610,7 @@ "while_upgrading": "While Upgrading", "why_is_this_needed": "Why is this needed?", "window_remains_open_mathesar_unusable": "This window will remain open but all features within Mathesar will be unusable.", + "yes": "Yes", "yes_summarize_as_list": "Yes, summarize as a list", "yesterday": "Yesterday" } diff --git a/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte b/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte index 2efe2a7c06..67915d4daf 100644 --- a/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte +++ b/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte @@ -53,6 +53,7 @@ restrictWidth cssVariables={{ '--max-layout-width': 'var(--max-layout-width-console-pages)', + '--layout-background-color': 'var(--sand-50)' }} > import { _ } from 'svelte-i18n'; + + import GridTable from '@mathesar/components/grid-table/GridTable.svelte'; + import GridTableCell from '@mathesar/components/grid-table/GridTableCell.svelte'; + import AsyncRpcApiStore from '@mathesar/stores/AsyncRpcApiStore'; + import { Button, Spinner } from '@mathesar-component-library'; + + import { getDatabaseSettingsContext } from './databaseSettingsUtils'; + import SettingsContentLayout from './SettingsContentLayout.svelte'; + + const databaseContext = getDatabaseSettingsContext(); + $: ({ database, configuredRoles, roles, combinedRoles } = $databaseContext); + + $: void AsyncRpcApiStore.runBatched( + [ + configuredRoles.batchRunner({ server_id: database.server.id }), + roles.batchRunner({ database_id: database.id }), + ], + { onlyRunIfNotInitialized: true }, + ); + $: isLoading = $configuredRoles.isLoading || $roles.isLoading; -{$_('role_configuration')} + + + {$_('role_configuration')} + + {#if isLoading} + + {:else} + {#if $combinedRoles.length > 0} +
+ + {$_('role')} + {$_('actions')} + + {#each $combinedRoles as combinedRole (combinedRole.name)} + {combinedRole.name} + + {#if combinedRole.configuredRole} +
+ + +
+ {:else if combinedRole.role} + + {/if} +
+ {/each} +
+
+ {/if} + + {#if $configuredRoles.error} + {$configuredRoles.error} + {/if} + {#if $roles.error} + {$roles.error} + {/if} + {/if} +
+ + diff --git a/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte b/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte index 69b1a23ffc..786b7f3ebc 100644 --- a/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte +++ b/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte @@ -10,7 +10,10 @@ } from '@mathesar/routes/urls'; import { LinkMenuItem, Menu, MenuHeading } from '@mathesar-component-library'; + import { setDatabaseSettingsContext } from './databaseSettingsUtils'; + export let database: Database; + $: setDatabaseSettingsContext(database); type Section = 'roleConfiguration' | 'collaborators' | 'roles'; let section = 'roleConfiguration'; @@ -63,6 +66,7 @@ --Menu__item-active-background: var(--sand-200); --Menu__item-active-hover-background: var(--sand-200); --Menu__item-focus-outline-color: var(--sand-300); + padding: var(--size-x-small) 0; .heading { font-size: var(--text-size-small); diff --git a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts new file mode 100644 index 0000000000..2bc64cf4b6 --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts @@ -0,0 +1,76 @@ +import { getContext, setContext } from 'svelte'; +import { type Readable, type Writable, derived, writable } from 'svelte/store'; + +import { ConfiguredRole } from '@mathesar/models/ConfiguredRole'; +import type { Database } from '@mathesar/models/Database'; +import { Role } from '@mathesar/models/Role'; + +const contextKey = Symbol('database settings store'); + +class DatabaseSettingsContext { + database: Database; + + configuredRoles; + + roles; + + combinedRoles: Readable< + { name: string; role?: Role; configuredRole?: ConfiguredRole }[] + >; + + constructor(database: Database) { + this.database = database; + this.configuredRoles = database.fetchConfiguredRoles(); + this.roles = database.fetchRoles(); + this.combinedRoles = derived( + [this.roles, this.configuredRoles], + ([$roles, $configuredRoles]) => { + const isLoading = $configuredRoles.isLoading || $roles.isLoading; + if (isLoading) { + return []; + } + const isStable = $configuredRoles.isStable && $roles.isStable; + const roles = $roles.resolvedValue; + const configuredRoles = $configuredRoles.resolvedValue?.mapKeys( + (cr) => cr.name, + ); + if (isStable && roles && configuredRoles) { + return [...roles.values()].map((role) => ({ + name: role.name, + role, + configuredRole: configuredRoles.get(role.name), + })); + } + if ($configuredRoles.isStable && configuredRoles) { + [...configuredRoles.values()].map((configuredRole) => ({ + name: configuredRole.name, + configuredRole, + })); + } + return []; + }, + ); + } +} + +export function getDatabaseSettingsContext(): Readable { + const store = getContext>(contextKey); + if (store === undefined) { + throw Error('Database settings context has not been set'); + } + return store; +} + +export function setDatabaseSettingsContext( + database: Database, +): Readable { + let store = getContext>(contextKey); + const databaseSettingsContext = new DatabaseSettingsContext(database); + if (store !== undefined) { + store.set(databaseSettingsContext); + return store; + } + store = writable(databaseSettingsContext); + setContext(contextKey, store); + return store; +} From 3509aabe50966aec5810c9341cb6a5ceff0af3ad Mon Sep 17 00:00:00 2001 From: pavish Date: Fri, 16 Aug 2024 23:32:09 +0530 Subject: [PATCH 18/36] Implement basic Roles page --- .../src/pages/database/settings/Roles.svelte | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/mathesar_ui/src/pages/database/settings/Roles.svelte b/mathesar_ui/src/pages/database/settings/Roles.svelte index 3636bb90da..0aadca8891 100644 --- a/mathesar_ui/src/pages/database/settings/Roles.svelte +++ b/mathesar_ui/src/pages/database/settings/Roles.svelte @@ -1,5 +1,67 @@ -{$_('roles')} + + + {$_('roles')} + + + + + {#if $roles.isLoading} + + {:else if $roles.isOk} +
+ + {$_('role')} + + LOGIN + + {$_('child_roles')} + + {$_('actions')} + {#each roleList as role (role.name)} + {role.name} + + {role.login ? $_('yes') : $_('no')} + + + {#each role.members ?? [] as member (member.oid)} + {member.oid} + {/each} + + + + + {/each} + +
+ {:else if $roles.error} + {$roles.error} + {/if} +
+ + From 8ae3daa6dbb8cf4cf15a0d64091a9d96e002cd7a Mon Sep 17 00:00:00 2001 From: pavish Date: Sat, 17 Aug 2024 02:20:14 +0530 Subject: [PATCH 19/36] Add collaborators api and model --- mathesar_ui/src/api/rpc/collaborators.ts | 20 ++++++++++++++++++++ mathesar_ui/src/api/rpc/index.ts | 2 ++ mathesar_ui/src/models/Collaborator.ts | 23 +++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 mathesar_ui/src/api/rpc/collaborators.ts create mode 100644 mathesar_ui/src/models/Collaborator.ts diff --git a/mathesar_ui/src/api/rpc/collaborators.ts b/mathesar_ui/src/api/rpc/collaborators.ts new file mode 100644 index 0000000000..1fbfe47a68 --- /dev/null +++ b/mathesar_ui/src/api/rpc/collaborators.ts @@ -0,0 +1,20 @@ +import { rpcMethodTypeContainer } from '@mathesar/packages/json-rpc-client-builder'; + +import type { RawConfiguredRole } from './configured_roles'; +import type { RawDatabase } from './databases'; + +export interface RawCollaborator { + id: number; + user_id: number; + database_id: RawDatabase['id']; + configured_role_id: RawConfiguredRole['id']; +} + +export const collaborators = { + list: rpcMethodTypeContainer< + { + database_id: RawDatabase['id']; + }, + Array + >(), +}; diff --git a/mathesar_ui/src/api/rpc/index.ts b/mathesar_ui/src/api/rpc/index.ts index cb0400b48f..a48b958e06 100644 --- a/mathesar_ui/src/api/rpc/index.ts +++ b/mathesar_ui/src/api/rpc/index.ts @@ -2,6 +2,7 @@ import Cookies from 'js-cookie'; import { buildRpcApi } from '@mathesar/packages/json-rpc-client-builder'; +import { collaborators } from './collaborators'; import { columns } from './columns'; import { configured_roles } from './configured_roles'; import { constraints } from './constraints'; @@ -18,6 +19,7 @@ export const api = buildRpcApi({ endpoint: '/api/rpc/v0/', getHeaders: () => ({ 'X-CSRFToken': Cookies.get('csrftoken') }), methodTree: { + collaborators, configured_roles, database_setup, databases, diff --git a/mathesar_ui/src/models/Collaborator.ts b/mathesar_ui/src/models/Collaborator.ts new file mode 100644 index 0000000000..48cf7ce151 --- /dev/null +++ b/mathesar_ui/src/models/Collaborator.ts @@ -0,0 +1,23 @@ +import type { RawCollaborator } from '@mathesar/api/rpc/collaborators'; + +import type { Database } from './Database'; + +export class Collaborator { + readonly id; + + readonly user_id; + + readonly configured_role_id; + + readonly database; + + constructor(props: { + database: Database; + rawCollaborator: RawCollaborator; + }) { + this.id = props.rawCollaborator.id; + this.user_id = props.rawCollaborator.user_id; + this.configured_role_id = props.rawCollaborator.configured_role_id; + this.database = props.database; + } +} From 3b5a443c1bae10f4f084fd0cd976b9ab9430a707 Mon Sep 17 00:00:00 2001 From: pavish Date: Sun, 18 Aug 2024 01:28:09 +0530 Subject: [PATCH 20/36] Implement Role Configuration page --- mathesar_ui/src/i18n/languages/en/dict.json | 10 ++ mathesar_ui/src/icons/index.ts | 1 + mathesar_ui/src/models/ConfiguredRole.ts | 14 ++ mathesar_ui/src/models/Role.ts | 31 ++++ mathesar_ui/src/models/Server.ts | 4 + .../pages/database/DatabasePageWrapper.svelte | 6 +- .../settings/RoleConfiguration.svelte | 75 -------- .../settings/databaseSettingsUtils.ts | 45 ++++- .../ConfigureRoleModal.svelte | 85 +++++++++ .../RoleConfiguration.svelte | 169 ++++++++++++++++++ mathesar_ui/src/routes/DatabaseRoute.svelte | 2 +- mathesar_ui/src/stores/AsyncStore.ts | 10 +- 12 files changed, 366 insertions(+), 86 deletions(-) delete mode 100644 mathesar_ui/src/pages/database/settings/RoleConfiguration.svelte create mode 100644 mathesar_ui/src/pages/database/settings/role-configuration/ConfigureRoleModal.svelte create mode 100644 mathesar_ui/src/pages/database/settings/role-configuration/RoleConfiguration.svelte diff --git a/mathesar_ui/src/i18n/languages/en/dict.json b/mathesar_ui/src/i18n/languages/en/dict.json index 3e5c6117be..fc051a044f 100644 --- a/mathesar_ui/src/i18n/languages/en/dict.json +++ b/mathesar_ui/src/i18n/languages/en/dict.json @@ -32,6 +32,7 @@ "ascending": "Ascending", "ascending_id": "Ascending ID", "attempt_exploration_recovery": "Attempt Exploration recovery", + "authenticate": "Authenticate", "automatically": "Automatically", "base_table_exploration_help": "The base table is the table that is being explored and determines the columns that are available for exploration.", "based_on": "Based on", @@ -78,6 +79,7 @@ "columns_to_move": "Columns to Move", "configure_in_mathesar": "Configure in Mathesar", "configure_password": "Configure Password", + "configure_value": "Configure ''{value}''", "confirm_and_create_table": "Confirm & create table", "confirm_delete_table": "To confirm the deletion of the [tableName] table, please enter the table name into the input field below.", "confirm_password": "Confirm Password", @@ -142,6 +144,7 @@ "databases": "Databases", "databases_matching_search": "{count, plural, one {{count} database matches [searchValue]} other {{count} databases match [searchValue]}}", "days": "Days", + "db_server": "DB server", "default": "Default", "default_value": "Default Value", "delete": "Delete", @@ -428,10 +431,13 @@ "release_notes_and_upgrade_instructions": "Release Notes and Upgrade Instructions", "released_date": "Released {date}", "remove": "Remove", + "remove_configuration": "Remove Configuration", + "remove_configuration_for_identifier": "Remove Configuration for [identifier]?", "remove_filters": "{count, plural, one {Remove Filter} other {Remove {count} Filters}}", "remove_grouping": "Remove Grouping", "remove_old_link_create_new": "Remove old link and create a new link?", "remove_sorting_type": "Remove {sortingType} Sorting", + "removing_role_configuration_warning": "Removing this configuration will prevent collaborators assigned to this role from accessing databases on server ''{server}''.", "rename_schema": "Rename [schemaName] Schema", "reset": "Reset", "restrict_to_unique": "Restrict to Unique", @@ -442,6 +448,10 @@ "reuse_credentials_from_known_connection": "Reuse credentials from a known connection", "role": "Role", "role_configuration": "Role Configuration", + "role_configuration_removed": "The role configuration has been removed successfully", + "role_configured_all_databases_in_server": "This role will be configured for all databases on DB Server ''{server}''", + "role_configured_successfully": "Role configured successfully", + "role_configured_successfully_new_password": "Role configured successfully with new password", "roles": "Roles", "row": "Row", "running_latest_version": "You are running the latest version", diff --git a/mathesar_ui/src/icons/index.ts b/mathesar_ui/src/icons/index.ts index ed004cbb89..9048599c0f 100644 --- a/mathesar_ui/src/icons/index.ts +++ b/mathesar_ui/src/icons/index.ts @@ -105,6 +105,7 @@ export const iconAddFilter: IconProps = { data: faFilter }; export const iconAddNew: IconProps = { data: faPlus }; export const iconAddUser: IconProps = { data: faUserPlus }; export const iconConfigure: IconProps = { data: faCogs }; +export const iconConfigurePassword = { data: faKey }; export const iconConnectDatabase = { data: connectDatabaseIcon }; export const iconCopyMajor: IconProps = { data: faCopy }; /** TODO: use faBinary once it's available (via newer FontAwesome version) */ diff --git a/mathesar_ui/src/models/ConfiguredRole.ts b/mathesar_ui/src/models/ConfiguredRole.ts index 6217f9e760..3ebfaf5a46 100644 --- a/mathesar_ui/src/models/ConfiguredRole.ts +++ b/mathesar_ui/src/models/ConfiguredRole.ts @@ -1,3 +1,4 @@ +import { api } from '@mathesar/api/rpc'; import type { RawConfiguredRole } from '@mathesar/api/rpc/configured_roles'; import type { Database } from './Database'; @@ -17,4 +18,17 @@ export class ConfiguredRole { this.name = props.rawConfiguredRole.name; this.database = props.database; } + + setPassword(password: string) { + return api.configured_roles + .set_password({ + configured_role_id: this.id, + password, + }) + .run(); + } + + delete() { + return api.configured_roles.delete({ configured_role_id: this.id }).run(); + } } diff --git a/mathesar_ui/src/models/Role.ts b/mathesar_ui/src/models/Role.ts index a39f997fcf..683f018311 100644 --- a/mathesar_ui/src/models/Role.ts +++ b/mathesar_ui/src/models/Role.ts @@ -1,5 +1,8 @@ +import { api } from '@mathesar/api/rpc'; import type { RawRole, RawRoleMember } from '@mathesar/api/rpc/roles'; +import { CancellablePromise } from '@mathesar/component-library'; +import { ConfiguredRole } from './ConfiguredRole'; import type { Database } from './Database'; export class Role { @@ -35,4 +38,32 @@ export class Role { this.members = props.rawRole.members; this.database = props.database; } + + configure(password: string): CancellablePromise { + const promise = api.configured_roles + .add({ + server_id: this.database.server.id, + name: this.name, + password, + }) + .run(); + + return new CancellablePromise( + (resolve, reject) => { + promise + .then( + (rawConfiguredRole) => + resolve( + new ConfiguredRole({ + database: this.database, + rawConfiguredRole, + }), + ), + reject, + ) + .catch(reject); + }, + () => promise.cancel(), + ); + } } diff --git a/mathesar_ui/src/models/Server.ts b/mathesar_ui/src/models/Server.ts index 203e13d398..1d51f3ed00 100644 --- a/mathesar_ui/src/models/Server.ts +++ b/mathesar_ui/src/models/Server.ts @@ -12,4 +12,8 @@ export class Server { this.host = props.rawServer.host; this.port = props.rawServer.port; } + + getConnectionString() { + return `${this.host}:${this.port}`; + } } diff --git a/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte b/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte index 67915d4daf..df871fcf34 100644 --- a/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte +++ b/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte @@ -53,7 +53,7 @@ restrictWidth cssVariables={{ '--max-layout-width': 'var(--max-layout-width-console-pages)', - '--layout-background-color': 'var(--sand-50)' + '--layout-background-color': 'var(--sand-50)', }} >
diff --git a/mathesar_ui/src/pages/database/settings/RoleConfiguration.svelte b/mathesar_ui/src/pages/database/settings/RoleConfiguration.svelte deleted file mode 100644 index 199e28d18e..0000000000 --- a/mathesar_ui/src/pages/database/settings/RoleConfiguration.svelte +++ /dev/null @@ -1,75 +0,0 @@ - - - - - {$_('role_configuration')} - - {#if isLoading} - - {:else} - {#if $combinedRoles.length > 0} -
- - {$_('role')} - {$_('actions')} - - {#each $combinedRoles as combinedRole (combinedRole.name)} - {combinedRole.name} - - {#if combinedRole.configuredRole} -
- - -
- {:else if combinedRole.role} - - {/if} -
- {/each} -
-
- {/if} - - {#if $configuredRoles.error} - {$configuredRoles.error} - {/if} - {#if $roles.error} - {$roles.error} - {/if} - {/if} -
- - diff --git a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts index 2bc64cf4b6..8c3a8bad1b 100644 --- a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts +++ b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts @@ -7,6 +7,12 @@ import { Role } from '@mathesar/models/Role'; const contextKey = Symbol('database settings store'); +export type CombinedLoginRole = { + name: string; + role?: Role; + configuredRole?: ConfiguredRole; +}; + class DatabaseSettingsContext { database: Database; @@ -14,15 +20,15 @@ class DatabaseSettingsContext { roles; - combinedRoles: Readable< - { name: string; role?: Role; configuredRole?: ConfiguredRole }[] - >; + combinedLoginRoles: Readable; + + collaborators; constructor(database: Database) { this.database = database; this.configuredRoles = database.fetchConfiguredRoles(); this.roles = database.fetchRoles(); - this.combinedRoles = derived( + this.combinedLoginRoles = derived( [this.roles, this.configuredRoles], ([$roles, $configuredRoles]) => { const isLoading = $configuredRoles.isLoading || $roles.isLoading; @@ -30,12 +36,14 @@ class DatabaseSettingsContext { return []; } const isStable = $configuredRoles.isStable && $roles.isStable; - const roles = $roles.resolvedValue; + const loginRoles = $roles.resolvedValue?.filterValues( + (value) => value.login, + ); const configuredRoles = $configuredRoles.resolvedValue?.mapKeys( (cr) => cr.name, ); - if (isStable && roles && configuredRoles) { - return [...roles.values()].map((role) => ({ + if (isStable && loginRoles && configuredRoles) { + return [...loginRoles.values()].map((role) => ({ name: role.name, role, configuredRole: configuredRoles.get(role.name), @@ -50,6 +58,29 @@ class DatabaseSettingsContext { return []; }, ); + this.collaborators = database.fetchCollaborators(); + } + + async configureRole(combinedLoginRole: CombinedLoginRole, password: string) { + if (combinedLoginRole.configuredRole) { + return combinedLoginRole.configuredRole.setPassword(password); + } + + if (combinedLoginRole.role) { + const configuredRole = await combinedLoginRole.role.configure(password); + this.configuredRoles.updateResolvedValue((configuredRoles) => + configuredRoles.with(configuredRole.id, configuredRole), + ); + } + + return undefined; + } + + async removeConfiguredRole(configuredRole: ConfiguredRole) { + await configuredRole.delete(); + this.configuredRoles.updateResolvedValue((configuredRoles) => + configuredRoles.without(configuredRole.id), + ); } } diff --git a/mathesar_ui/src/pages/database/settings/role-configuration/ConfigureRoleModal.svelte b/mathesar_ui/src/pages/database/settings/role-configuration/ConfigureRoleModal.svelte new file mode 100644 index 0000000000..434047a119 --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/role-configuration/ConfigureRoleModal.svelte @@ -0,0 +1,85 @@ + + + form.reset()}> + + {$_('configure_value', { + values: { value: combinedLoginRole.name }, + })} + +
+ + + + {$_('role_configured_all_databases_in_server', { + values: { + server: database.server.getConnectionString(), + }, + })} + + +
+ +
diff --git a/mathesar_ui/src/pages/database/settings/role-configuration/RoleConfiguration.svelte b/mathesar_ui/src/pages/database/settings/role-configuration/RoleConfiguration.svelte new file mode 100644 index 0000000000..e98b2ec426 --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/role-configuration/RoleConfiguration.svelte @@ -0,0 +1,169 @@ + + + + + {$_('role_configuration')} + + {#if isLoading} + + {:else} + {#if $combinedLoginRoles.length > 0} +
+ + {$_('role')} + {$_('actions')} + + {#each $combinedLoginRoles as combinedLoginRole (combinedLoginRole.name)} + + + {combinedLoginRole.name} + + + + {#if combinedLoginRole.configuredRole} +
+ + + confirm({ + title: { + component: PhraseContainingIdentifier, + props: { + identifier: combinedLoginRole.name, + wrappingString: $_( + 'remove_configuration_for_identifier', + ), + }, + }, + body: $_('removing_role_configuration_warning', { + values: { + server: + combinedLoginRole.configuredRole?.database.server.getConnectionString(), + }, + }), + proceedButton: { + label: $_('remove_configuration'), + icon: iconDeleteMajor, + }, + })} + label={$_('remove')} + onClick={() => removeConfiguredRole(combinedLoginRole)} + /> +
+ {:else if combinedLoginRole.role} + + {/if} +
+ {/each} +
+
+ {/if} + + {#if $configuredRoles.error} + + {$configuredRoles.error} + + {/if} + {#if $roles.error} + + {$roles.error} + + {/if} + {/if} +
+ +{#if targetCombinedLoginRole} + +{/if} + + diff --git a/mathesar_ui/src/routes/DatabaseRoute.svelte b/mathesar_ui/src/routes/DatabaseRoute.svelte index 28b73a564b..40c48b206a 100644 --- a/mathesar_ui/src/routes/DatabaseRoute.svelte +++ b/mathesar_ui/src/routes/DatabaseRoute.svelte @@ -11,7 +11,7 @@ import DatabasePageWrapper from '@mathesar/pages/database/DatabasePageWrapper.svelte'; import DatabasePageSchemasSection from '@mathesar/pages/database/schemas/SchemasSection.svelte'; import DatabaseCollaborators from '@mathesar/pages/database/settings/Collaborators.svelte'; - import DatabaseRoleConfiguration from '@mathesar/pages/database/settings/RoleConfiguration.svelte'; + import DatabaseRoleConfiguration from '@mathesar/pages/database/settings/role-configuration/RoleConfiguration.svelte'; import DatabaseRoles from '@mathesar/pages/database/settings/Roles.svelte'; import DatabasePageSettingsWrapper from '@mathesar/pages/database/settings/SettingsWrapper.svelte'; import ErrorPage from '@mathesar/pages/ErrorPage.svelte'; diff --git a/mathesar_ui/src/stores/AsyncStore.ts b/mathesar_ui/src/stores/AsyncStore.ts index 9ede933009..3d382ede09 100644 --- a/mathesar_ui/src/stores/AsyncStore.ts +++ b/mathesar_ui/src/stores/AsyncStore.ts @@ -177,12 +177,20 @@ export default class AsyncStore this.value.set(new AsyncStoreValue({ isLoading: false })); } + updateResolvedValue(updater: (resolvedValue: T) => T) { + const value = get(this.value); + if (value.isOk && value.resolvedValue) { + const updatedValue = updater(value.resolvedValue); + this.setResolvedValue(updatedValue); + } + } + protected beforeRun() { this.cancel(); this.value.update((v) => new AsyncStoreValue({ ...v, isLoading: true })); } - setResolvedValue(value: T) { + protected setResolvedValue(value: T) { this.cancel(); this.value.set( new AsyncStoreValue({ From dbeae24f511b11231cbab5488716bfd4c3137a7b Mon Sep 17 00:00:00 2001 From: pavish Date: Sun, 18 Aug 2024 01:28:59 +0530 Subject: [PATCH 21/36] Implement base collaborator page --- mathesar_ui/src/models/Database.ts | 15 ++++- .../database/settings/Collaborators.svelte | 5 -- .../collaborators/Collaborators.svelte | 67 +++++++++++++++++++ mathesar_ui/src/routes/DatabaseRoute.svelte | 2 +- 4 files changed, 82 insertions(+), 7 deletions(-) delete mode 100644 mathesar_ui/src/pages/database/settings/Collaborators.svelte create mode 100644 mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte diff --git a/mathesar_ui/src/models/Database.ts b/mathesar_ui/src/models/Database.ts index c8b1771553..1f72416ccf 100644 --- a/mathesar_ui/src/models/Database.ts +++ b/mathesar_ui/src/models/Database.ts @@ -1,8 +1,9 @@ import { api } from '@mathesar/api/rpc'; import type { RawDatabase } from '@mathesar/api/rpc/databases'; import AsyncRpcApiStore from '@mathesar/stores/AsyncRpcApiStore'; -import { SortedImmutableMap } from '@mathesar-component-library'; +import { ImmutableMap, SortedImmutableMap } from '@mathesar-component-library'; +import { Collaborator } from './Collaborator'; import { ConfiguredRole } from './ConfiguredRole'; import { Role } from './Role'; import type { Server } from './Server'; @@ -45,4 +46,16 @@ export class Database { ), }); } + + fetchCollaborators() { + return new AsyncRpcApiStore(api.collaborators.list, { + postProcess: (rawCollaborators) => + new ImmutableMap( + rawCollaborators.map((rawCollaborator) => [ + rawCollaborator.id, + new Collaborator({ database: this, rawCollaborator }), + ]), + ), + }); + } } diff --git a/mathesar_ui/src/pages/database/settings/Collaborators.svelte b/mathesar_ui/src/pages/database/settings/Collaborators.svelte deleted file mode 100644 index 5c75a672eb..0000000000 --- a/mathesar_ui/src/pages/database/settings/Collaborators.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - -{$_('collaborators')} diff --git a/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte b/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte new file mode 100644 index 0000000000..b87613e613 --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte @@ -0,0 +1,67 @@ + + + + + {$_('collaborators')} + + + + + {#if isLoading} + + {:else if $collaborators.isOk} +
+ + {$_('mathesar_user')} + {$_('role')} + {$_('actions')} + {#each [...($collaborators.resolvedValue?.values() ?? [])] as collaborator (collaborator.id)} + {collaborator.user_id} + + {collaborator.configured_role_id} + + + + + {/each} + +
+ {:else if $roles.error} + {$roles.error} + {/if} +
+ + diff --git a/mathesar_ui/src/routes/DatabaseRoute.svelte b/mathesar_ui/src/routes/DatabaseRoute.svelte index 40c48b206a..9e0b759987 100644 --- a/mathesar_ui/src/routes/DatabaseRoute.svelte +++ b/mathesar_ui/src/routes/DatabaseRoute.svelte @@ -10,7 +10,7 @@ import type { Database } from '@mathesar/models/Database'; import DatabasePageWrapper from '@mathesar/pages/database/DatabasePageWrapper.svelte'; import DatabasePageSchemasSection from '@mathesar/pages/database/schemas/SchemasSection.svelte'; - import DatabaseCollaborators from '@mathesar/pages/database/settings/Collaborators.svelte'; + import DatabaseCollaborators from '@mathesar/pages/database/settings/collaborators/Collaborators.svelte'; import DatabaseRoleConfiguration from '@mathesar/pages/database/settings/role-configuration/RoleConfiguration.svelte'; import DatabaseRoles from '@mathesar/pages/database/settings/Roles.svelte'; import DatabasePageSettingsWrapper from '@mathesar/pages/database/settings/SettingsWrapper.svelte'; From 77dbbf1e3e050e2556408c9e86074964192865bb Mon Sep 17 00:00:00 2001 From: pavish Date: Sun, 18 Aug 2024 16:36:41 +0530 Subject: [PATCH 22/36] Display user and role information in collaborators page --- mathesar_ui/src/components/Errors.svelte | 3 +- mathesar_ui/src/i18n/languages/en/dict.json | 2 + .../src/pages/database/settings/Roles.svelte | 5 +- .../collaborators/CollaboratorRow.svelte | 63 +++++++++++++++++++ .../collaborators/Collaborators.svelte | 39 +++++++----- .../settings/databaseSettingsUtils.ts | 25 ++++++++ 6 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte diff --git a/mathesar_ui/src/components/Errors.svelte b/mathesar_ui/src/components/Errors.svelte index 10fb4c0834..f196d95a32 100644 --- a/mathesar_ui/src/components/Errors.svelte +++ b/mathesar_ui/src/components/Errors.svelte @@ -2,10 +2,11 @@ import ErrorBox from '@mathesar/components/message-boxes/ErrorBox.svelte'; export let errors: string[]; + export let fullWidth = false; {#if errors.length} - + {#if errors.length === 1} {errors[0]} {:else} diff --git a/mathesar_ui/src/i18n/languages/en/dict.json b/mathesar_ui/src/i18n/languages/en/dict.json index fc051a044f..5d4912658c 100644 --- a/mathesar_ui/src/i18n/languages/en/dict.json +++ b/mathesar_ui/src/i18n/languages/en/dict.json @@ -9,6 +9,7 @@ "action_cannot_be_undone": "This action cannot be undone", "actions": "Actions", "add": "Add", + "add_collaborator": "Add Collaborator", "add_columns_to_exploration_empty_message": "This exploration does not contain any columns. Edit the exploration to add columns to it.", "add_filter": "Add Filter", "add_new_filter": "Add New Filter", @@ -306,6 +307,7 @@ "many_to_one": "Many to One", "many_to_one_link_description": "Multiple [baseTable] records can link to the same [targetTable] record.", "mathesar": "Mathesar", + "mathesar_user": "Mathesar User", "max_time_unit": "Max Time Unit", "milliseconds": "Milliseconds", "min_time_unit": "Min Time Unit", diff --git a/mathesar_ui/src/pages/database/settings/Roles.svelte b/mathesar_ui/src/pages/database/settings/Roles.svelte index 0aadca8891..c0dc96f557 100644 --- a/mathesar_ui/src/pages/database/settings/Roles.svelte +++ b/mathesar_ui/src/pages/database/settings/Roles.svelte @@ -3,6 +3,7 @@ import GridTable from '@mathesar/components/grid-table/GridTable.svelte'; import GridTableCell from '@mathesar/components/grid-table/GridTableCell.svelte'; + import ErrorBox from '@mathesar/components/message-boxes/ErrorBox.svelte'; import { Button, Spinner } from '@mathesar-component-library'; import { getDatabaseSettingsContext } from './databaseSettingsUtils'; @@ -55,7 +56,9 @@
{:else if $roles.error} - {$roles.error} + + {$roles.error} + {/if} diff --git a/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte b/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte new file mode 100644 index 0000000000..aaf3a69df3 --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte @@ -0,0 +1,63 @@ + + + +
+ {#if user} +
{user.full_name ?? user.username}
+
{user.email}
+ {:else} + {collaborator.user_id} + {/if} +
+
+ +
+
+ {#if configuredRole} + {configuredRole.name} + {:else} + {collaborator.configured_role_id} + {/if} +
+
+ +
+
+
+ + + + + diff --git a/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte b/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte index b87613e613..019283858c 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte @@ -2,26 +2,38 @@ import { _ } from 'svelte-i18n'; import Icon from '@mathesar/component-library/icon/Icon.svelte'; + import Errors from '@mathesar/components/Errors.svelte'; import GridTable from '@mathesar/components/grid-table/GridTable.svelte'; import GridTableCell from '@mathesar/components/grid-table/GridTableCell.svelte'; - import { iconDeleteMajor } from '@mathesar/icons'; + import { iconAddNew } from '@mathesar/icons'; import AsyncRpcApiStore from '@mathesar/stores/AsyncRpcApiStore'; + import { isDefined } from '@mathesar/utils/language'; import { Button, Spinner } from '@mathesar-component-library'; import { getDatabaseSettingsContext } from '../databaseSettingsUtils'; import SettingsContentLayout from '../SettingsContentLayout.svelte'; + import CollaboratorRow from './CollaboratorRow.svelte'; + const databaseContext = getDatabaseSettingsContext(); - $: ({ database, roles, collaborators } = $databaseContext); + $: ({ database, configuredRoles, collaborators, users } = $databaseContext); $: void AsyncRpcApiStore.runBatched( [ collaborators.batchRunner({ database_id: database.id }), - roles.batchRunner({ database_id: database.id }), + configuredRoles.batchRunner({ server_id: database.server.id }), ], { onlyRunIfNotInitialized: true }, ); - $: isLoading = $collaborators.isLoading || $roles.isLoading; + $: void users.runIfNotInitialized(); + $: isLoading = + $collaborators.isLoading || $configuredRoles.isLoading || $users.isLoading; + $: isSuccess = $collaborators.isOk && $configuredRoles.isOk && $users.isOk; + $: errors = [ + $collaborators.error, + $configuredRoles.error, + $users.error, + ].filter((entry): entry is string => isDefined(entry)); @@ -30,32 +42,25 @@ {#if isLoading} - {:else if $collaborators.isOk} + {:else if isSuccess}
{$_('mathesar_user')} {$_('role')} {$_('actions')} {#each [...($collaborators.resolvedValue?.values() ?? [])] as collaborator (collaborator.id)} - {collaborator.user_id} - - {collaborator.configured_role_id} - - - - + {/each}
- {:else if $roles.error} - {$roles.error} + {:else} + {/if}
diff --git a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts index 8c3a8bad1b..d21a914256 100644 --- a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts +++ b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts @@ -1,9 +1,12 @@ import { getContext, setContext } from 'svelte'; import { type Readable, type Writable, derived, writable } from 'svelte/store'; +import userApi, { type User } from '@mathesar/api/rest/users'; import { ConfiguredRole } from '@mathesar/models/ConfiguredRole'; import type { Database } from '@mathesar/models/Database'; import { Role } from '@mathesar/models/Role'; +import AsyncStore from '@mathesar/stores/AsyncStore'; +import { CancellablePromise, ImmutableMap } from '@mathesar-component-library'; const contextKey = Symbol('database settings store'); @@ -13,6 +16,25 @@ export type CombinedLoginRole = { configuredRole?: ConfiguredRole; }; +// TODO: Make CancellablePromise chainable +const getUsersPromise = () => { + const promise = userApi.list(); + return new CancellablePromise>( + (resolve, reject) => { + promise + .then( + (response) => + resolve( + new ImmutableMap(response.results.map((user) => [user.id, user])), + ), + (err) => reject(err), + ) + .catch((err) => reject(err)); + }, + () => promise.cancel(), + ); +}; + class DatabaseSettingsContext { database: Database; @@ -24,6 +46,8 @@ class DatabaseSettingsContext { collaborators; + users: AsyncStore>; + constructor(database: Database) { this.database = database; this.configuredRoles = database.fetchConfiguredRoles(); @@ -59,6 +83,7 @@ class DatabaseSettingsContext { }, ); this.collaborators = database.fetchCollaborators(); + this.users = new AsyncStore(getUsersPromise); } async configureRole(combinedLoginRole: CombinedLoginRole, password: string) { From cd97e2d062b25f2500e3668add0ecea60d3d7591 Mon Sep 17 00:00:00 2001 From: pavish Date: Sun, 18 Aug 2024 18:59:20 +0530 Subject: [PATCH 23/36] Implement add collaborator modal --- mathesar_ui/src/api/rpc/collaborators.ts | 8 ++ mathesar_ui/src/i18n/languages/en/dict.json | 2 + mathesar_ui/src/models/Database.ts | 37 +++++- .../collaborators/AddCollaboratorModal.svelte | 110 ++++++++++++++++++ .../collaborators/CollaboratorRow.svelte | 2 +- .../collaborators/Collaborators.svelte | 23 +++- .../settings/databaseSettingsUtils.ts | 13 +++ 7 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 mathesar_ui/src/pages/database/settings/collaborators/AddCollaboratorModal.svelte diff --git a/mathesar_ui/src/api/rpc/collaborators.ts b/mathesar_ui/src/api/rpc/collaborators.ts index 1fbfe47a68..fceb67c9dc 100644 --- a/mathesar_ui/src/api/rpc/collaborators.ts +++ b/mathesar_ui/src/api/rpc/collaborators.ts @@ -17,4 +17,12 @@ export const collaborators = { }, Array >(), + add: rpcMethodTypeContainer< + { + database_id: RawDatabase['id']; + user_id: number; + configured_role_id: RawConfiguredRole['id']; + }, + RawCollaborator + >(), }; diff --git a/mathesar_ui/src/i18n/languages/en/dict.json b/mathesar_ui/src/i18n/languages/en/dict.json index 5d4912658c..7dc4793ca0 100644 --- a/mathesar_ui/src/i18n/languages/en/dict.json +++ b/mathesar_ui/src/i18n/languages/en/dict.json @@ -54,6 +54,7 @@ "cleaning_up": "Cleaning up", "clear": "Clear", "clear_value": "Clear value", + "collaborator_added_successfully": "Collaborator added successfully", "collaborators": "Collaborators", "column": "Column", "column_added_number_of_times": "{count, plural, one {This column has been added once.} other {This column has been added {count} times.}}", @@ -492,6 +493,7 @@ "select_columns_to_hide": "Select Columns to Hide", "select_columns_view_properties": "Select a column to view it's properties.", "select_permission": "Select Permission", + "select_role": "Select Role", "select_table": "Select Table", "select_type": "Select Type", "select_user": "Select User", diff --git a/mathesar_ui/src/models/Database.ts b/mathesar_ui/src/models/Database.ts index 1f72416ccf..b39c443b6d 100644 --- a/mathesar_ui/src/models/Database.ts +++ b/mathesar_ui/src/models/Database.ts @@ -1,7 +1,11 @@ import { api } from '@mathesar/api/rpc'; import type { RawDatabase } from '@mathesar/api/rpc/databases'; import AsyncRpcApiStore from '@mathesar/stores/AsyncRpcApiStore'; -import { ImmutableMap, SortedImmutableMap } from '@mathesar-component-library'; +import { + CancellablePromise, + ImmutableMap, + SortedImmutableMap, +} from '@mathesar-component-library'; import { Collaborator } from './Collaborator'; import { ConfiguredRole } from './ConfiguredRole'; @@ -58,4 +62,35 @@ export class Database { ), }); } + + addCollaborator( + userId: number, + configuredRoleId: ConfiguredRole['id'], + ): CancellablePromise { + const promise = api.collaborators + .add({ + database_id: this.id, + user_id: userId, + configured_role_id: configuredRoleId, + }) + .run(); + + return new CancellablePromise( + (resolve, reject) => { + promise + .then( + (rawCollaborator) => + resolve( + new Collaborator({ + database: this, + rawCollaborator, + }), + ), + reject, + ) + .catch(reject); + }, + () => promise.cancel(), + ); + } } diff --git a/mathesar_ui/src/pages/database/settings/collaborators/AddCollaboratorModal.svelte b/mathesar_ui/src/pages/database/settings/collaborators/AddCollaboratorModal.svelte new file mode 100644 index 0000000000..6b5052735a --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/collaborators/AddCollaboratorModal.svelte @@ -0,0 +1,110 @@ + + + form.reset()}> + + {$_('add_collaborator')} + +
+ user.id), + getLabel: (option) => { + if (option) { + return usersMap.get(option)?.username ?? String(option); + } + return $_('select_user'); + }, + autoSelect: 'none', + }, + }} + /> + r.id), + getLabel: (option) => { + if (option) { + return configuredRolesMap.get(option)?.name ?? String(option); + } + return $_('select_role'); + }, + autoSelect: 'none', + }, + }} + /> +
+ +
diff --git a/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte b/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte index aaf3a69df3..a4cc0590ff 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte @@ -21,7 +21,7 @@
{#if user} -
{user.full_name ?? user.username}
+
{user.full_name || user.username}
{user.email}
{:else} {collaborator.user_id} diff --git a/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte b/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte index 019283858c..a26d9c8078 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte @@ -7,15 +7,19 @@ import GridTableCell from '@mathesar/components/grid-table/GridTableCell.svelte'; import { iconAddNew } from '@mathesar/icons'; import AsyncRpcApiStore from '@mathesar/stores/AsyncRpcApiStore'; + import { modal } from '@mathesar/stores/modal'; import { isDefined } from '@mathesar/utils/language'; import { Button, Spinner } from '@mathesar-component-library'; import { getDatabaseSettingsContext } from '../databaseSettingsUtils'; import SettingsContentLayout from '../SettingsContentLayout.svelte'; + import AddCollaboratorModel from './AddCollaboratorModal.svelte'; import CollaboratorRow from './CollaboratorRow.svelte'; const databaseContext = getDatabaseSettingsContext(); + const addCollaboratorModal = modal.spawnModalController(); + $: ({ database, configuredRoles, collaborators, users } = $databaseContext); $: void AsyncRpcApiStore.runBatched( @@ -41,10 +45,12 @@ {$_('collaborators')} - + {#if isSuccess} + + {/if} {#if isLoading} @@ -64,6 +70,15 @@ {/if} +{#if $users.resolvedValue && $configuredRoles.resolvedValue && $collaborators.resolvedValue} + +{/if} + diff --git a/mathesar_ui/src/routes/DatabaseRoute.svelte b/mathesar_ui/src/routes/DatabaseRoute.svelte index 9e0b759987..bc2f3c465a 100644 --- a/mathesar_ui/src/routes/DatabaseRoute.svelte +++ b/mathesar_ui/src/routes/DatabaseRoute.svelte @@ -12,7 +12,7 @@ import DatabasePageSchemasSection from '@mathesar/pages/database/schemas/SchemasSection.svelte'; import DatabaseCollaborators from '@mathesar/pages/database/settings/collaborators/Collaborators.svelte'; import DatabaseRoleConfiguration from '@mathesar/pages/database/settings/role-configuration/RoleConfiguration.svelte'; - import DatabaseRoles from '@mathesar/pages/database/settings/Roles.svelte'; + import DatabaseRoles from '@mathesar/pages/database/settings/roles/Roles.svelte'; import DatabasePageSettingsWrapper from '@mathesar/pages/database/settings/SettingsWrapper.svelte'; import ErrorPage from '@mathesar/pages/ErrorPage.svelte'; import { databasesStore } from '@mathesar/stores/databases'; From 3566d3d1a1f74f574e9b2deb77044edd0fdb226b Mon Sep 17 00:00:00 2001 From: pavish Date: Sun, 18 Aug 2024 22:41:54 +0530 Subject: [PATCH 28/36] fix type errors --- mathesar_ui/src/models/Collaborator.ts | 2 +- .../pages/database/settings/SettingsWrapper.svelte | 2 +- .../collaborators/AddCollaboratorModal.svelte | 6 +++--- .../settings/collaborators/CollaboratorRow.svelte | 2 +- .../settings/collaborators/Collaborators.svelte | 2 +- .../EditRoleForCollaboratorModal.svelte | 6 +++--- .../collaborators/SelectConfiguredRoleField.svelte | 6 +++--- .../database/settings/databaseSettingsUtils.ts | 6 +++--- .../database/settings/roles/CreateRoleModal.svelte | 2 +- mathesar_ui/src/routes/RootRoute.svelte | 8 ++++---- mathesar_ui/src/stores/databases.ts | 14 +++++++++----- 11 files changed, 30 insertions(+), 26 deletions(-) diff --git a/mathesar_ui/src/models/Collaborator.ts b/mathesar_ui/src/models/Collaborator.ts index 7577703ace..4ba7dae50e 100644 --- a/mathesar_ui/src/models/Collaborator.ts +++ b/mathesar_ui/src/models/Collaborator.ts @@ -2,7 +2,7 @@ import { api } from '@mathesar/api/rpc'; import type { RawCollaborator } from '@mathesar/api/rpc/collaborators'; import { CancellablePromise } from '@mathesar/component-library'; -import { ConfiguredRole } from './ConfiguredRole'; +import type { ConfiguredRole } from './ConfiguredRole'; import type { Database } from './Database'; export class Collaborator { diff --git a/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte b/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte index 786b7f3ebc..1831c267a2 100644 --- a/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte +++ b/mathesar_ui/src/pages/database/settings/SettingsWrapper.svelte @@ -2,7 +2,7 @@ import { _ } from 'svelte-i18n'; import PageLayoutWithSidebar from '@mathesar/layouts/PageLayoutWithSidebar.svelte'; - import { Database } from '@mathesar/models/Database'; + import type { Database } from '@mathesar/models/Database'; import { getDatabaseCollaboratorsUrl, getDatabaseRoleConfigurationUrl, diff --git a/mathesar_ui/src/pages/database/settings/collaborators/AddCollaboratorModal.svelte b/mathesar_ui/src/pages/database/settings/collaborators/AddCollaboratorModal.svelte index 00e45edce3..79b4825a90 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/AddCollaboratorModal.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/AddCollaboratorModal.svelte @@ -8,12 +8,12 @@ requiredField, } from '@mathesar/components/form'; import Field from '@mathesar/components/form/Field.svelte'; - import { Collaborator } from '@mathesar/models/Collaborator'; - import { ConfiguredRole } from '@mathesar/models/ConfiguredRole'; + import type { Collaborator } from '@mathesar/models/Collaborator'; + import type { ConfiguredRole } from '@mathesar/models/ConfiguredRole'; import { toast } from '@mathesar/stores/toast'; import { ControlledModal, - ImmutableMap, + type ImmutableMap, type ModalController, Select, portalToWindowFooter, diff --git a/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte b/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte index 90e17bf5f7..3bce77a219 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte @@ -4,7 +4,7 @@ import Icon from '@mathesar/component-library/icon/Icon.svelte'; import GridTableCell from '@mathesar/components/grid-table/GridTableCell.svelte'; import { iconDeleteMajor, iconEdit } from '@mathesar/icons'; - import { Collaborator } from '@mathesar/models/Collaborator'; + import type { Collaborator } from '@mathesar/models/Collaborator'; import { confirmDelete } from '@mathesar/stores/confirmation'; import { Button, SpinnerButton } from '@mathesar-component-library'; diff --git a/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte b/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte index ca8ca6cfe0..f74c581d0e 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/Collaborators.svelte @@ -6,7 +6,7 @@ import GridTable from '@mathesar/components/grid-table/GridTable.svelte'; import GridTableCell from '@mathesar/components/grid-table/GridTableCell.svelte'; import { iconAddNew } from '@mathesar/icons'; - import { Collaborator } from '@mathesar/models/Collaborator'; + import type { Collaborator } from '@mathesar/models/Collaborator'; import AsyncRpcApiStore from '@mathesar/stores/AsyncRpcApiStore'; import { modal } from '@mathesar/stores/modal'; import { isDefined } from '@mathesar/utils/language'; diff --git a/mathesar_ui/src/pages/database/settings/collaborators/EditRoleForCollaboratorModal.svelte b/mathesar_ui/src/pages/database/settings/collaborators/EditRoleForCollaboratorModal.svelte index 667692e72c..9d6ca95e6c 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/EditRoleForCollaboratorModal.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/EditRoleForCollaboratorModal.svelte @@ -7,12 +7,12 @@ makeForm, requiredField, } from '@mathesar/components/form'; - import { Collaborator } from '@mathesar/models/Collaborator'; - import { ConfiguredRole } from '@mathesar/models/ConfiguredRole'; + import type { Collaborator } from '@mathesar/models/Collaborator'; + import type { ConfiguredRole } from '@mathesar/models/ConfiguredRole'; import { toast } from '@mathesar/stores/toast'; import { ControlledModal, - ImmutableMap, + type ImmutableMap, type ModalController, portalToWindowFooter, } from '@mathesar-component-library'; diff --git a/mathesar_ui/src/pages/database/settings/collaborators/SelectConfiguredRoleField.svelte b/mathesar_ui/src/pages/database/settings/collaborators/SelectConfiguredRoleField.svelte index a231aafbbb..b508350f47 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/SelectConfiguredRoleField.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/SelectConfiguredRoleField.svelte @@ -1,10 +1,10 @@ + + form.reset()}> + + {$_('edit_child_roles_for_parent', { + values: { + parent: parentRole.name, + }, + })} + +
+ +
+ {#if $memberOids.size > 0} + {#each [...$memberOids] as memberOid (memberOid)} +
+ + {rolesMap.get(memberOid)?.name ?? memberOid} + + + + +
+ {/each} + {:else} + + {$_('role_has_no_child_roles')} + + {/if} +
+
+ +
+ + diff --git a/mathesar_ui/src/pages/database/settings/roles/RoleRow.svelte b/mathesar_ui/src/pages/database/settings/roles/RoleRow.svelte new file mode 100644 index 0000000000..88ead522d6 --- /dev/null +++ b/mathesar_ui/src/pages/database/settings/roles/RoleRow.svelte @@ -0,0 +1,105 @@ + + +{role.name} + + {#if role.login} + {$_('yes')} + {/if} + + +
+
+ {#each [...$members.values()] as member (member.oid)} + + {rolesMap.get(member.oid)?.name ?? ''} + + {/each} +
+
+ +
+
+
+ + + confirm({ + title: { + component: PhraseContainingIdentifier, + props: { + identifier: role.name, + wrappingString: $_('drop_role_with_identifier'), + }, + }, + body: [ + $_('action_cannot_be_undone'), + $_('drop_role_warning'), + $_('are_you_sure_to_proceed'), + ], + proceedButton: { + label: $_('drop_role'), + icon: iconDeleteMajor, + }, + })} + label={$_('drop_role')} + onClick={dropRole} + /> + + + diff --git a/mathesar_ui/src/pages/database/settings/roles/Roles.svelte b/mathesar_ui/src/pages/database/settings/roles/Roles.svelte index 3a07394bb8..6739b68f8b 100644 --- a/mathesar_ui/src/pages/database/settings/roles/Roles.svelte +++ b/mathesar_ui/src/pages/database/settings/roles/Roles.svelte @@ -4,7 +4,8 @@ import GridTable from '@mathesar/components/grid-table/GridTable.svelte'; import GridTableCell from '@mathesar/components/grid-table/GridTableCell.svelte'; import ErrorBox from '@mathesar/components/message-boxes/ErrorBox.svelte'; - import { iconAddNew, iconEdit } from '@mathesar/icons'; + import { iconAddNew } from '@mathesar/icons'; + import type { Role } from '@mathesar/models/Role'; import { modal } from '@mathesar/stores/modal'; import { Button, Icon, Spinner } from '@mathesar-component-library'; @@ -12,14 +13,24 @@ import SettingsContentLayout from '../SettingsContentLayout.svelte'; import CreateRoleModal from './CreateRoleModal.svelte'; + import ModifyRoleMembers from './ModifyRoleMembers.svelte'; + import RoleRow from './RoleRow.svelte'; const databaseContext = getDatabaseSettingsContext(); const createRoleModalController = modal.spawnModalController(); + const modifyRoleMembersModalController = modal.spawnModalController(); $: ({ database, roles } = $databaseContext); $: void roles.runIfNotInitialized({ database_id: database.id }); $: roleList = [...($roles.resolvedValue?.values() ?? [])]; + + let targetRole: Role | undefined = undefined; + + function modifyMembersForRole(role: Role) { + targetRole = role; + modifyRoleMembersModalController.open(); + } @@ -39,7 +50,7 @@ {#if $roles.isLoading} - {:else if $roles.isOk} + {:else if $roles.isOk && $roles.resolvedValue}
{$_('role')} @@ -49,32 +60,12 @@ {$_('child_roles')} {$_('actions')} - {#each roleList as role (role.name)} - {role.name} - - {role.login ? $_('yes') : $_('no')} - - -
-
- {#each role.members ?? [] as member (member.oid)} - - {$roles.resolvedValue?.get(member.oid)?.name ?? ''} - - {/each} -
-
- -
-
-
- - - + {#each roleList as role (role.oid)} + {/each}
@@ -87,24 +78,17 @@ +{#if $roles.resolvedValue && targetRole} + +{/if} + From d7f14ae969178393cbb3fda9017f2c1ea5afe842 Mon Sep 17 00:00:00 2001 From: pavish Date: Thu, 22 Aug 2024 19:05:13 +0530 Subject: [PATCH 32/36] Reset stores when items in dependent stores are deleted --- .../pages/database/settings/databaseSettingsUtils.ts | 10 ++++++++++ .../role-configuration/ConfigureRoleModal.svelte | 2 -- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts index 8821495ebd..8699581c6f 100644 --- a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts +++ b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts @@ -107,6 +107,13 @@ class DatabaseSettingsContext { this.configuredRoles.updateResolvedValue((configuredRoles) => configuredRoles.without(configuredRole.id), ); + /** + * When a configured role is removed from the Role Configuration page, + * Collaborators list needs to be reset, since the drop statement cascades. + * + * TODO_BETA: Discuss on whether we should cascade or throw error? + */ + this.collaborators.reset(); } async addCollaborator( @@ -141,6 +148,9 @@ class DatabaseSettingsContext { async deleteRole(role: Role) { await role.delete(); this.roles.updateResolvedValue((r) => r.without(role.oid)); + // When a role is deleted, both Collaborators & ConfiguredRoles needs to be reset + this.configuredRoles.reset(); + this.collaborators.reset(); } } diff --git a/mathesar_ui/src/pages/database/settings/role-configuration/ConfigureRoleModal.svelte b/mathesar_ui/src/pages/database/settings/role-configuration/ConfigureRoleModal.svelte index 434047a119..c7f2eab7a5 100644 --- a/mathesar_ui/src/pages/database/settings/role-configuration/ConfigureRoleModal.svelte +++ b/mathesar_ui/src/pages/database/settings/role-configuration/ConfigureRoleModal.svelte @@ -39,7 +39,6 @@ } else { toast.success($_('role_configured_successfully')); } - form.reset(); } @@ -74,7 +73,6 @@ {form} catchErrors onCancel={() => { - form.reset(); controller.close(); }} onProceed={configureRole} From ad3d5fbc223b36075e0930b7c8f9105751e2b8fb Mon Sep 17 00:00:00 2001 From: pavish Date: Thu, 22 Aug 2024 19:22:28 +0530 Subject: [PATCH 33/36] Make request for adding roles --- mathesar_ui/src/i18n/languages/en/dict.json | 3 +- mathesar_ui/src/models/Database.ts | 33 +++++++++++++++++++ .../settings/databaseSettingsUtils.ts | 17 ++++++++++ .../settings/roles/CreateRoleModal.svelte | 21 ++++++++++-- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/mathesar_ui/src/i18n/languages/en/dict.json b/mathesar_ui/src/i18n/languages/en/dict.json index ecf6615823..4f6eab083f 100644 --- a/mathesar_ui/src/i18n/languages/en/dict.json +++ b/mathesar_ui/src/i18n/languages/en/dict.json @@ -465,7 +465,8 @@ "role_configured_all_databases_in_server": "This role will be configured for all databases on DB Server ''{server}''", "role_configured_successfully": "Role configured successfully", "role_configured_successfully_new_password": "Role configured successfully with new password", - "role_dropped_successfully": "The role has been dropped successfully", + "role_created_successfully": "The Role has been created successfully", + "role_dropped_successfully": "The Role has been dropped successfully", "role_has_no_child_roles": "The Role does not have any Child Roles", "role_name": "Role Name", "roles": "Roles", diff --git a/mathesar_ui/src/models/Database.ts b/mathesar_ui/src/models/Database.ts index b39c443b6d..fe856c478a 100644 --- a/mathesar_ui/src/models/Database.ts +++ b/mathesar_ui/src/models/Database.ts @@ -93,4 +93,37 @@ export class Database { () => promise.cancel(), ); } + + addRole( + roleName: string, + login: boolean, + password?: string, + ): CancellablePromise { + const promise = api.roles + .add({ + database_id: this.id, + rolename: roleName, + login, + password, + }) + .run(); + + return new CancellablePromise( + (resolve, reject) => { + promise + .then( + (rawRole) => + resolve( + new Role({ + database: this, + rawRole, + }), + ), + reject, + ) + .catch(reject); + }, + () => promise.cancel(), + ); + } } diff --git a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts index 8699581c6f..9a86a04984 100644 --- a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts +++ b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts @@ -145,6 +145,23 @@ class DatabaseSettingsContext { this.collaborators.updateResolvedValue((c) => c.without(collaborator.id)); } + async addRole( + props: + | { + roleName: Role['name']; + login: false; + password?: never; + } + | { roleName: Role['name']; login: true; password: string }, + ) { + const newRole = await this.database.addRole( + props.roleName, + props.login, + props.password, + ); + this.roles.updateResolvedValue((r) => r.with(newRole.oid, newRole)); + } + async deleteRole(role: Role) { await role.delete(); this.roles.updateResolvedValue((r) => r.without(role.oid)); diff --git a/mathesar_ui/src/pages/database/settings/roles/CreateRoleModal.svelte b/mathesar_ui/src/pages/database/settings/roles/CreateRoleModal.svelte index e715031b29..ccc193b4f1 100644 --- a/mathesar_ui/src/pages/database/settings/roles/CreateRoleModal.svelte +++ b/mathesar_ui/src/pages/database/settings/roles/CreateRoleModal.svelte @@ -9,6 +9,7 @@ makeForm, requiredField, } from '@mathesar/components/form'; + import { toast } from '@mathesar/stores/toast'; import { Checkbox, ControlledModal, @@ -18,6 +19,10 @@ portalToWindowFooter, } from '@mathesar-component-library'; + import { getDatabaseSettingsContext } from '../databaseSettingsUtils'; + + const databaseContext = getDatabaseSettingsContext(); + export let controller: ModalController; const roleName = requiredField(''); @@ -43,8 +48,20 @@ ]); $: login, password.reset(), confirmPassword.reset(); - function createRole() { - // + async function createRole() { + if (login) { + await $databaseContext.addRole({ + roleName: $roleName, + login: true, + password: $password, + }); + } else { + await $databaseContext.addRole({ + roleName: $roleName, + login: false, + }); + } + toast.success('role_created_successfully'); } From 551b5f798be6ae922bb27e17a9fb0ae9845f6b37 Mon Sep 17 00:00:00 2001 From: pavish Date: Thu, 22 Aug 2024 19:45:07 +0530 Subject: [PATCH 34/36] Use a store for configured_role_id property in Collaborator model --- mathesar_ui/src/models/Collaborator.ts | 27 ++++++++++--------- .../collaborators/CollaboratorRow.svelte | 7 +++-- .../EditRoleForCollaboratorModal.svelte | 14 +++------- .../settings/databaseSettingsUtils.ts | 11 -------- 4 files changed, 20 insertions(+), 39 deletions(-) diff --git a/mathesar_ui/src/models/Collaborator.ts b/mathesar_ui/src/models/Collaborator.ts index 4ba7dae50e..2fcbefac21 100644 --- a/mathesar_ui/src/models/Collaborator.ts +++ b/mathesar_ui/src/models/Collaborator.ts @@ -1,3 +1,5 @@ +import { type Readable, type Writable, writable } from 'svelte/store'; + import { api } from '@mathesar/api/rpc'; import type { RawCollaborator } from '@mathesar/api/rpc/collaborators'; import { CancellablePromise } from '@mathesar/component-library'; @@ -10,15 +12,20 @@ export class Collaborator { readonly user_id; - // TODO: Use a store for configured_role_id - readonly configured_role_id; + readonly _configured_role_id: Writable; + + get configured_role_id(): Readable { + return this._configured_role_id; + } readonly database; constructor(props: { database: Database; rawCollaborator: RawCollaborator }) { this.id = props.rawCollaborator.id; this.user_id = props.rawCollaborator.user_id; - this.configured_role_id = props.rawCollaborator.configured_role_id; + this._configured_role_id = writable( + props.rawCollaborator.configured_role_id, + ); this.database = props.database; } @@ -35,16 +42,10 @@ export class Collaborator { return new CancellablePromise( (resolve, reject) => { promise - .then( - (rawCollaborator) => - resolve( - new Collaborator({ - database: this.database, - rawCollaborator, - }), - ), - reject, - ) + .then((rawCollaborator) => { + this._configured_role_id.set(rawCollaborator.configured_role_id); + return resolve(this); + }, reject) .catch(reject); }, () => promise.cancel(), diff --git a/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte b/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte index 3bce77a219..7a1c494d23 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/CollaboratorRow.svelte @@ -17,9 +17,8 @@ $: ({ configuredRoles, users } = $databaseContext); $: user = $users.resolvedValue?.get(collaborator.user_id); - $: configuredRole = $configuredRoles.resolvedValue?.get( - collaborator.configured_role_id, - ); + $: configuredRoleId = collaborator.configured_role_id; + $: configuredRole = $configuredRoles.resolvedValue?.get($configuredRoleId); $: userName = user ? user.full_name || user.username : ''; @@ -39,7 +38,7 @@ {#if configuredRole} {configuredRole.name} {:else} - {collaborator.configured_role_id} + {$configuredRoleId} {/if}
diff --git a/mathesar_ui/src/pages/database/settings/collaborators/EditRoleForCollaboratorModal.svelte b/mathesar_ui/src/pages/database/settings/collaborators/EditRoleForCollaboratorModal.svelte index 9d6ca95e6c..8fe7fe0c8f 100644 --- a/mathesar_ui/src/pages/database/settings/collaborators/EditRoleForCollaboratorModal.svelte +++ b/mathesar_ui/src/pages/database/settings/collaborators/EditRoleForCollaboratorModal.svelte @@ -17,18 +17,15 @@ portalToWindowFooter, } from '@mathesar-component-library'; - import { getDatabaseSettingsContext } from '../databaseSettingsUtils'; - import SelectConfiguredRoleField from './SelectConfiguredRoleField.svelte'; - const databaseContext = getDatabaseSettingsContext(); - export let controller: ModalController; export let collaborator: Collaborator; export let configuredRolesMap: ImmutableMap; export let usersMap: ImmutableMap; - $: configuredRoleId = requiredField(collaborator.configured_role_id); + $: savedConfiguredRoleId = collaborator.configured_role_id; + $: configuredRoleId = requiredField($savedConfiguredRoleId); $: form = makeForm({ configuredRoleId }); $: userName = @@ -36,13 +33,9 @@ String(collaborator.user_id); async function updateRoleForCollaborator() { - await $databaseContext.updateRoleForCollaborator( - collaborator, - $configuredRoleId, - ); + await collaborator.setConfiguredRole($configuredRoleId); controller.close(); toast.success($_('collaborator_role_updated_successfully')); - form.reset(); } @@ -62,7 +55,6 @@ {form} catchErrors onCancel={() => { - form.reset(); controller.close(); }} onProceed={updateRoleForCollaborator} diff --git a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts index 9a86a04984..91f5221fbd 100644 --- a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts +++ b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts @@ -129,17 +129,6 @@ class DatabaseSettingsContext { ); } - async updateRoleForCollaborator( - collaborator: Collaborator, - configuredRoleId: ConfiguredRole['id'], - ) { - const updatedCollaborator = - await collaborator.setConfiguredRole(configuredRoleId); - this.collaborators.updateResolvedValue((collaborators) => - collaborators.with(updatedCollaborator.id, updatedCollaborator), - ); - } - async deleteCollaborator(collaborator: Collaborator) { await collaborator.delete(); this.collaborators.updateResolvedValue((c) => c.without(collaborator.id)); From 4c32dc9a7612973be3df9d222d1369c2070ec259 Mon Sep 17 00:00:00 2001 From: pavish Date: Thu, 22 Aug 2024 19:52:09 +0530 Subject: [PATCH 35/36] Improve names of model methods --- mathesar_ui/src/models/Database.ts | 6 +++--- .../src/pages/database/settings/databaseSettingsUtils.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mathesar_ui/src/models/Database.ts b/mathesar_ui/src/models/Database.ts index fe856c478a..be35d9b2e9 100644 --- a/mathesar_ui/src/models/Database.ts +++ b/mathesar_ui/src/models/Database.ts @@ -25,7 +25,7 @@ export class Database { this.server = props.server; } - fetchConfiguredRoles() { + constructConfiguredRolesStore() { return new AsyncRpcApiStore(api.configured_roles.list, { postProcess: (rawConfiguredRoles) => new SortedImmutableMap( @@ -38,7 +38,7 @@ export class Database { }); } - fetchRoles() { + constructRolesStore() { return new AsyncRpcApiStore(api.roles.list, { postProcess: (rawRoles) => new SortedImmutableMap( @@ -51,7 +51,7 @@ export class Database { }); } - fetchCollaborators() { + constructCollaboratorsStore() { return new AsyncRpcApiStore(api.collaborators.list, { postProcess: (rawCollaborators) => new ImmutableMap( diff --git a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts index 91f5221fbd..8edce0b131 100644 --- a/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts +++ b/mathesar_ui/src/pages/database/settings/databaseSettingsUtils.ts @@ -51,8 +51,8 @@ class DatabaseSettingsContext { constructor(database: Database) { this.database = database; - this.configuredRoles = database.fetchConfiguredRoles(); - this.roles = database.fetchRoles(); + this.configuredRoles = database.constructConfiguredRolesStore(); + this.roles = database.constructRolesStore(); this.combinedLoginRoles = derived( [this.roles, this.configuredRoles], ([$roles, $configuredRoles]) => { @@ -75,7 +75,7 @@ class DatabaseSettingsContext { })); } if ($configuredRoles.isStable && configuredRoles) { - [...configuredRoles.values()].map((configuredRole) => ({ + return [...configuredRoles.values()].map((configuredRole) => ({ name: configuredRole.name, configuredRole, })); @@ -83,7 +83,7 @@ class DatabaseSettingsContext { return []; }, ); - this.collaborators = database.fetchCollaborators(); + this.collaborators = database.constructCollaboratorsStore(); this.users = new AsyncStore(getUsersPromise); } From e7603767e788a5bd1b45703bc89ada6cebac763e Mon Sep 17 00:00:00 2001 From: pavish Date: Thu, 22 Aug 2024 20:06:48 +0530 Subject: [PATCH 36/36] Temporarily hide Database permissions button and db actions --- mathesar_ui/src/pages/database/DatabasePageWrapper.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte b/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte index df871fcf34..40e33c1342 100644 --- a/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte +++ b/mathesar_ui/src/pages/database/DatabasePageWrapper.svelte @@ -67,7 +67,7 @@ }} >
-