diff --git a/webapp/packages/core-blocks/src/Table/TableItem.tsx b/webapp/packages/core-blocks/src/Table/TableItem.tsx
index 3cf917496e..f3817011b4 100644
--- a/webapp/packages/core-blocks/src/Table/TableItem.tsx
+++ b/webapp/packages/core-blocks/src/Table/TableItem.tsx
@@ -78,10 +78,10 @@ export const TableItem = observer(function TableItem({
>
{children}
- {isExpanded && (
+ {isExpanded && ExpandElement && (
|
- {ExpandElement && }
+
|
)}
diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEdit.tsx b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEdit.tsx
index f0aef06ad7..ecc04c9736 100644
--- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEdit.tsx
+++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEdit.tsx
@@ -146,19 +146,19 @@ export const ConnectionEdit = observer(function ConnectionEdit({
{controller.isLoading
diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEditController.ts b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEditController.ts
index 3d281aca86..15aa831ad3 100644
--- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEditController.ts
+++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEditController.ts
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
-import { observable, action } from 'mobx';
+import { observable, action, computed } from 'mobx';
import {
injectable, IInitializableController, IDestructibleController, Bootstrap
@@ -47,6 +47,10 @@ implements IInitializableController, IDestructibleController {
credentials: {},
};
+ @computed get isDisabled() {
+ return this.isLoading || this.isSaving;
+ }
+
get isNew() {
return this.connectionsResource.isNew(this.connectionId);
}
@@ -180,13 +184,12 @@ implements IInitializableController, IDestructibleController {
await this.connectionsResource.load(this.connectionId);
this.connectionInfo = this.connectionsResource.get(this.connectionId)!;
+ await this.loadDriver(this.connectionInfo.driverId);
} catch (exception) {
this.notificationService.logException(exception, `Can't load ConnectionInfo ${this.connectionId}`);
} finally {
this.isLoading = false;
}
-
- await this.loadDriver(this.connectionInfo.driverId);
}
private async loadDriver(driverId: string) {
diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionForm/ConnectionForm.tsx b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionForm/ConnectionForm.tsx
index c6ca725a78..59b4147361 100644
--- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionForm/ConnectionForm.tsx
+++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionForm/ConnectionForm.tsx
@@ -42,7 +42,7 @@ export const ConnectionForm = observer(function ConnectionForm({
checkboxLabel={translate('connections_connection_template')}
checked={controller.config.template}
onChange={value => controller.onChange('template', value)}
- disabled={!controller.isNew || controller.isSaving}
+ disabled={!controller.isNew || controller.isDisabled}
mod='surface'
/>
@@ -65,7 +65,7 @@ export const ConnectionForm = observer(function ConnectionForm({
name="name"
value={controller.config.name}
onChange={value => controller.onChange('name', value)}
- disabled={controller.isSaving}
+ disabled={controller.isDisabled}
mod='surface'
>
{translate('connections_connection_name')}
@@ -77,7 +77,7 @@ export const ConnectionForm = observer(function ConnectionForm({
rows={3}
value={controller.config.description}
onChange={value => controller.onChange('description', value)}
- disabled={controller.isSaving}
+ disabled={controller.isDisabled}
mod='surface'
>
{translate('connections_connection_description')}
@@ -92,7 +92,7 @@ export const ConnectionForm = observer(function ConnectionForm({
value={'custom'}
onClick={() => controller.onChangeType(ConnectionType.Attributes)}
checked={controller.connectionType === ConnectionType.Attributes}
- disabled={controller.isSaving}
+ disabled={controller.isDisabled}
mod={['primary']}
>
{translate('customConnection_connectionType_custom')}
@@ -103,7 +103,7 @@ export const ConnectionForm = observer(function ConnectionForm({
value={'url'}
onClick={() => controller.onChangeType(ConnectionType.URL)}
checked={controller.connectionType === ConnectionType.URL}
- disabled={controller.isSaving}
+ disabled={controller.isDisabled}
mod={['primary']}
>
{translate('customConnection_connectionType_url')}
@@ -118,7 +118,7 @@ export const ConnectionForm = observer(function ConnectionForm({
name="url"
value={controller.config.url}
onChange={value => controller.onChange('url', value)}
- disabled={controller.isSaving}
+ disabled={controller.isDisabled}
mod='surface'
>
{translate('customConnection_url_JDBC')}
@@ -135,7 +135,7 @@ export const ConnectionForm = observer(function ConnectionForm({
autofillToken={`section-${controller.driver?.id || ''} section-auth`}
properties={controller.authModel.properties}
credentials={controller.config.credentials}
- processing={controller.isSaving}
+ processing={controller.isDisabled}
/>
>
)}
diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionForm/IFormController.ts b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionForm/IFormController.ts
index 9609d90375..572e89fc62 100644
--- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionForm/IFormController.ts
+++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionForm/IFormController.ts
@@ -20,6 +20,7 @@ export interface IFormController {
config: ConnectionConfig;
connectionType: ConnectionType;
isSaving: boolean;
+ isDisabled: boolean;
onChangeType(type: ConnectionType): void;
onSelectDriver(driver: DBDriver): void;
onChange(property: T, value: ConnectionConfig[T]): void;
diff --git a/webapp/packages/core-connections/src/Administration/ConnectionsResource.ts b/webapp/packages/core-connections/src/Administration/ConnectionsResource.ts
index 8853e141bb..8fced80cdc 100644
--- a/webapp/packages/core-connections/src/Administration/ConnectionsResource.ts
+++ b/webapp/packages/core-connections/src/Administration/ConnectionsResource.ts
@@ -15,25 +15,40 @@ import {
ResourceKey,
isResourceKeyList,
} from '@cloudbeaver/core-sdk';
-import { uuid } from '@cloudbeaver/core-utils';
+import { uuid, MetadataMap } from '@cloudbeaver/core-utils';
+
+const NEW_CONNECTION_SYMBOL = Symbol('new-connection');
+type ConnectionNew = ConnectionInfo & { [NEW_CONNECTION_SYMBOL]: boolean }
@injectable()
export class ConnectionsResource extends CachedMapResource {
- constructor(
- private graphQLService: GraphQLService,
- ) {
+ private metadata: MetadataMap;
+ constructor(private graphQLService: GraphQLService) {
super(new Map());
+ this.metadata = new MetadataMap(() => false);
+ }
+
+ has(id: string) {
+ if (this.metadata.has(id)) {
+ return this.metadata.get(id);
+ }
+
+ return this.data.has(id);
}
isNew(id: string) {
- return id.startsWith('new-');
+ if (!this.has(id)) {
+ return false;
+ }
+ return NEW_CONNECTION_SYMBOL in this.get(id)!;
}
addNew() {
const connectionInfo = {
id: `new-${uuid()}`,
name: 'New connection',
- } as ConnectionInfo;
+ [NEW_CONNECTION_SYMBOL]: true,
+ } as ConnectionNew;
this.data.set(connectionInfo.id, connectionInfo);
this.markUpdated(connectionInfo.id);
@@ -52,7 +67,7 @@ export class ConnectionsResource extends CachedMapResource extends CachedResource<
isLoaded(key: ResourceKey): boolean {
if (isResourceKeyList(key)) {
- return key.list.every(key => this.data.has(key));
+ return key.list.every(key => this.has(key));
}
- return this.data.has(key);
+ return this.has(key);
}
isDataLoading(key: ResourceKey): boolean {
@@ -142,6 +142,10 @@ export abstract class CachedMapResource extends CachedResource<
await this.loadData(key);
return this.get(key) as Array| TValue;
}
+
+ has(key: TKey): boolean {
+ return this.data.has(key);
+ }
}
export function isResourceKeyList(data: any): data is ResourceKeyList {
diff --git a/webapp/packages/core-sdk/src/queries/authentication/users/revokeUserRole.gql b/webapp/packages/core-sdk/src/queries/authentication/users/revokeUserRole.gql
new file mode 100644
index 0000000000..2590b783a9
--- /dev/null
+++ b/webapp/packages/core-sdk/src/queries/authentication/users/revokeUserRole.gql
@@ -0,0 +1,3 @@
+query revokeUserRole($userId: ID!, $roleId: ID!) {
+ revokeUserRole(userId: $userId, roleId: $roleId)
+}
\ No newline at end of file
diff --git a/webapp/packages/core-sdk/src/sdk.ts b/webapp/packages/core-sdk/src/sdk.ts
index 7353c1d054..836a9de8d4 100644
--- a/webapp/packages/core-sdk/src/sdk.ts
+++ b/webapp/packages/core-sdk/src/sdk.ts
@@ -846,6 +846,13 @@ export type GrantUserRoleQueryVariables = Exact<{
export type GrantUserRoleQuery = Pick;
+export type RevokeUserRoleQueryVariables = Exact<{
+ userId: Scalars['ID'];
+ roleId: Scalars['ID'];
+}>;
+
+export type RevokeUserRoleQuery = Pick;
+
export type SetUserCredentialsQueryVariables = Exact<{
userId: Scalars['ID'];
providerId: Scalars['ID'];
@@ -1330,6 +1337,11 @@ export const GrantUserRoleDocument = `
grantUserRole(userId: $userId, roleId: $roleId)
}
`;
+export const RevokeUserRoleDocument = `
+ query revokeUserRole($userId: ID!, $roleId: ID!) {
+ revokeUserRole(userId: $userId, roleId: $roleId)
+}
+ `;
export const SetUserCredentialsDocument = `
query setUserCredentials($userId: ID!, $providerId: ID!, $credentials: Object!) {
setUserCredentials(userId: $userId, providerId: $providerId, credentials: $credentials)
@@ -2067,6 +2079,9 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
grantUserRole(variables: GrantUserRoleQueryVariables): Promise {
return withWrapper(() => client.request(GrantUserRoleDocument, variables));
},
+ revokeUserRole(variables: RevokeUserRoleQueryVariables): Promise {
+ return withWrapper(() => client.request(RevokeUserRoleDocument, variables));
+ },
setUserCredentials(variables: SetUserCredentialsQueryVariables): Promise {
return withWrapper(() => client.request(SetUserCredentialsDocument, variables));
},
diff --git a/webapp/packages/core-utils/src/MetadataMap.ts b/webapp/packages/core-utils/src/MetadataMap.ts
index e952895721..78d56b8fb2 100644
--- a/webapp/packages/core-utils/src/MetadataMap.ts
+++ b/webapp/packages/core-utils/src/MetadataMap.ts
@@ -39,6 +39,14 @@ export class MetadataMap {
return this.length;
}
+ has(key: TKey): boolean {
+ return this.data.has(key);
+ }
+
+ set(key: TKey, value: TValue) {
+ this.data.set(key, value);
+ }
+
get(key: TKey): TValue {
if (this.data.has(key)) {
return this.data.get(key)!;
diff --git a/webapp/packages/plugin-authentication/src/Administration/RolesResource.ts b/webapp/packages/plugin-authentication/src/Administration/RolesResource.ts
index 3aa2f65c7f..e316d31f24 100644
--- a/webapp/packages/plugin-authentication/src/Administration/RolesResource.ts
+++ b/webapp/packages/plugin-authentication/src/Administration/RolesResource.ts
@@ -9,40 +9,52 @@
import { injectable } from '@cloudbeaver/core-di';
import {
GraphQLService,
- CachedDataResource,
- AdminRoleInfo
+ AdminRoleInfo,
+ CachedMapResource,
+ ResourceKey
} from '@cloudbeaver/core-sdk';
+import { MetadataMap } from '@cloudbeaver/core-utils';
@injectable()
-export class RolesResource extends CachedDataResource {
- constructor(
- private graphQLService: GraphQLService,
- ) {
- super([]);
+export class RolesResource extends CachedMapResource {
+ private metadata: MetadataMap;
+ constructor(private graphQLService: GraphQLService) {
+ super(new Map());
+ this.metadata = new MetadataMap(() => false);
}
- isLoaded(roleId?: string) {
- return roleId
- ? this.data.some(role => role.roleId === roleId)
- : !!this.data.length;
+ has(id: string) {
+ if (this.metadata.has(id)) {
+ return this.metadata.get(id);
+ }
+
+ return this.data.has(id);
}
- protected async loader(roleId?: string): Promise {
+ async loadAll() {
+ await this.load('all');
+ return this.data;
+ }
+
+ async refreshAll() {
+ await this.refresh('all');
+ return this.data;
+ }
+
+ protected async loader(key: ResourceKey): Promise