Fix cloneable permissions between query types

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8284
GitOrigin-RevId: 17c0590bcfa032d9a396851dfc0c97b2ee94feb1
This commit is contained in:
Erik Magnusson 2023-03-17 09:43:26 +01:00 committed by hasura-bot
parent 18368d6c6d
commit 662754ef9e
16 changed files with 440 additions and 163 deletions

View File

@ -40,7 +40,7 @@ export const gdc: Database = {
return (table as GDCTable).join('_');
},
getSupportedQueryTypes: async () => {
return ['select'];
return ['select', 'delete', 'update', 'insert'];
},
},
};

View File

@ -97,12 +97,9 @@ const Component = (props: ComponentProps) => {
// E.g. when switching tables
useEffect(() => {
const newValues = getValues();
reset({ ...newValues, ...defaultValues });
reset({ ...newValues, ...defaultValues, clonePermissions: [] });
}, [roleName, defaultValues]);
// allRowChecks relates to other queries and is for duplicating from others
const allRowChecks = defaultValues?.allRowChecks;
const key = `${JSON.stringify(table)}-${queryType}-${roleName}`;
const filterType = getValues('filterType');
@ -140,7 +137,6 @@ const Component = (props: ComponentProps) => {
queryType={queryType}
subQueryType={queryType === 'update' ? 'pre' : undefined}
permissionsKey={filterKeys[0]}
allRowChecks={allRowChecks || []}
dataSourceName={dataSourceName}
supportedOperators={data?.defaultValues?.supportedOperators ?? []}
defaultValues={defaultValues}
@ -157,7 +153,6 @@ const Component = (props: ComponentProps) => {
roleName={roleName}
queryType={queryType}
subQueryType={queryType === 'update' ? 'post' : undefined}
allRowChecks={allRowChecks || []}
permissionsKey={filterKeys[1]}
dataSourceName={dataSourceName}
supportedOperators={

View File

@ -1,8 +1,8 @@
import produce from 'immer';
import produce, { Draft, original } from 'immer';
import { allowedMetadataTypes } from '../../../MetadataAPI';
import { AccessType } from '../../types';
import { AccessType, QueryType } from '../../types';
import { PermissionsSchema, Presets } from '../../schema';
import { areTablesEqual } from '../../../hasura-metadata-api';
import { Table } from '../../../hasura-metadata-types';
@ -205,6 +205,49 @@ export interface ExistingPermission {
role: string;
queryType: string;
}
/**
* When cloning permissions we have to transform the payload between filter and checks.
* The input per type is:
* select uses filter
* delete uses filter
* insert uses check
* update uses filter(for pre-check) and check (for post-check)
*
* When cloning between the permissions type we need to swap these out based on with way we are cloning
*/
const getPermissionsWithMappedRowPermissions = (
permissionsObject: any,
mainQueryType: QueryType,
clonedQueryType: string
): { filter?: Record<string, any>; check?: Record<string, any> } => {
const clone: { filter?: Record<string, any>; check?: Record<string, any> } = {
filter: permissionsObject.filter,
check: permissionsObject.check,
};
if (
(mainQueryType === 'select' && clonedQueryType === 'insert') ||
(mainQueryType === 'select' && clonedQueryType === 'update') ||
(mainQueryType === 'delete' && clonedQueryType === 'insert') ||
(mainQueryType === 'delete' && clonedQueryType === 'update')
) {
clone.check = permissionsObject.filter;
}
if (
(mainQueryType === 'update' && clonedQueryType === 'select') ||
(mainQueryType === 'update' && clonedQueryType === 'delete') ||
(mainQueryType === 'insert' && clonedQueryType === 'select') ||
(mainQueryType === 'insert' && clonedQueryType === 'delete') ||
(mainQueryType === 'insert' && clonedQueryType === 'update')
) {
clone.filter = permissionsObject.check;
}
return { filter: clone.filter, check: clone.check };
};
/**
* creates the insert arguments to update permissions
* adds cloned permissions
@ -221,7 +264,6 @@ export const createInsertArgs = ({
tables,
}: CreateInsertArgs) => {
const permission = createPermission(formData);
// create args object with args from form
const initialArgs = [
{
@ -274,9 +316,22 @@ export const createInsertArgs = ({
d.set = {};
}
return d;
const newValues = {
...getPermissionsWithMappedRowPermissions(
original(d),
queryType,
clonedPermission.queryType
),
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
d.filter = newValues.filter;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
d.check = newValues.check;
}
);
// add each closed permission to args
draft.push({
type: `${driver}_create_${clonedPermission.queryType}_permission` as allowedMetadataTypes,

View File

@ -7,12 +7,11 @@ import { useIsDisabled } from '../hooks/useIsDisabled';
import { QueryType } from '../../types';
import { Permission } from '../../schema';
import { Feature } from '../../../DataSource';
import { Table } from '../../../hasura-metadata-types';
import { getTableDisplayName } from '../../../DatabaseRelationships';
import { QualifiedTable } from '../../../../metadata/types';
interface ClonePermissionsRowProps {
id: number;
tables: Table[];
tables: QualifiedTable[];
currentQueryType: QueryType;
queryTypes: string[];
roleNames: string[];
@ -42,7 +41,6 @@ export const ClonePermissionsRow: React.FC<ClonePermissionsRowProps> = ({
<div>
<select
className={className}
disabled={allDisabled}
title={allDisabled ? 'Set a row permission first' : ''}
{...register(`${formKey}.${id}.tableName`)}
>
@ -64,7 +62,6 @@ export const ClonePermissionsRow: React.FC<ClonePermissionsRowProps> = ({
<div>
<select
className={className}
disabled={allDisabled}
title={allDisabled ? 'Set a row permission first' : ''}
{...register(`${formKey}.${id}.queryType`)}
>
@ -83,7 +80,6 @@ export const ClonePermissionsRow: React.FC<ClonePermissionsRowProps> = ({
<div>
<select
className={className}
disabled={allDisabled}
title={allDisabled ? 'Set a row permission first' : ''}
{...register(`${formKey}.${id}.roleName`)}
>
@ -113,7 +109,7 @@ export const ClonePermissionsRow: React.FC<ClonePermissionsRowProps> = ({
export interface ClonePermissionsSectionProps {
queryType: string;
tables: Table[];
tables: QualifiedTable[];
supportedQueryTypes: QueryType[] | Feature | undefined;
roles: string[];
defaultOpen?: boolean;

View File

@ -9,6 +9,7 @@ import { getEdForm } from '../../../../components/Services/Data/utils';
import { useIsDisabled } from '../hooks/useIsDisabled';
import { QueryType } from '../../types';
import { isPermissionModalDisabled } from '../utils/getPermissionModalStatus';
import { SelectColumn } from '../../../DataSource/types';
import {
getPermissionsModalTitle,
@ -171,7 +172,6 @@ export const ColumnPermissionsSection: React.FC<
<strong>columns</strong>:
</p>
</div>
<fieldset className="flex gap-4 flex-wrap">
{columns?.map(fieldName => (
<label key={fieldName} className="flex gap-2 items-center">

View File

@ -10,8 +10,6 @@ import {
RowPermissionsWrapperProps,
} from './RowPermissions';
import { QueryType } from '../../types';
export default {
title: 'Features/Permissions/Form/Row Section',
component: RowPermissionsSection,
@ -28,18 +26,6 @@ export default {
const roleName = 'two';
// this will be moved into a utils folder
const allRowChecks = [
{
queryType: 'insert' as QueryType,
value: '{"id":{"_eq":1}}',
},
{
queryType: 'select' as QueryType,
value: '{"id":{"_eq":1}}',
},
];
interface Props {
wrapper: RowPermissionsWrapperProps;
section: RowPermissionsProps;
@ -59,9 +45,6 @@ Insert.args = {
},
dataSourceName: 'chinook',
queryType: 'delete',
allRowChecks,
// allSchemas,
// allFunctions,
},
};
@ -75,7 +58,6 @@ Select.args = {
section: {
...Insert!.args!.section!,
queryType: 'select',
allRowChecks,
},
};
@ -87,9 +69,8 @@ export const Update: Story<Props> = args => (
Update.args = {
wrapper: { roleName, queryType: 'update', defaultOpen: true },
section: {
...Insert.args.section!,
...(Insert?.args?.section || {}),
queryType: 'update',
allRowChecks,
},
};
@ -101,9 +82,8 @@ export const Delete: Story<Props> = args => (
Delete.args = {
wrapper: { roleName, queryType: 'delete', defaultOpen: true },
section: {
...Insert.args.section!,
...(Insert?.args?.section || {}),
queryType: 'delete',
allRowChecks,
},
};

View File

@ -1,7 +1,7 @@
import React from 'react';
import AceEditor from 'react-ace';
import { useFormContext } from 'react-hook-form';
import { Table } from '../../../hasura-metadata-types';
import { MetadataTable, Table } from '../../../hasura-metadata-types';
import { useHttpClient } from '../../../Network';
import { useQuery } from 'react-query';
import { DataSource, exportMetadata, Operator } from '../../../DataSource';
@ -14,6 +14,8 @@ import { getIngForm } from '../../../../components/Services/Data/utils';
import { RowPermissionBuilder } from './RowPermissionsBuilder';
import { QueryType } from '../../types';
import { ReturnValue } from '../hooks';
import { useMetadataTable } from '../../../hasura-metadata-api/metadataHooks';
import { getNonSelectedQueryTypePermissions } from '../utils/getMapQueryTypePermissions';
const NoChecksLabel = () => (
<span data-test="without-checks">Without any checks&nbsp;</span>
@ -30,7 +32,6 @@ export interface RowPermissionsProps {
table: unknown;
queryType: QueryType;
subQueryType?: string;
allRowChecks: Array<{ queryType: QueryType; value: string }>;
dataSourceName: string;
supportedOperators: Operator[];
defaultValues: ReturnValue['defaultValues'];
@ -42,6 +43,10 @@ enum SelectedSection {
NoChecks = 'no_checks',
Custom = 'custom',
NoneSelected = 'none',
insert = 'insert',
select = 'select',
update = 'update',
delete = 'delete',
}
const getRowPermission = (queryType: QueryType, subQueryType?: string) => {
@ -134,14 +139,21 @@ export const RowPermissionsSection: React.FC<RowPermissionsProps> = ({
table,
queryType,
subQueryType,
allRowChecks,
dataSourceName,
defaultValues,
permissionsKey,
roleName,
}) => {
const { data: tableName, isLoading } = useTypeName({ table, dataSourceName });
const { register, watch, setValue } = useFormContext();
const metadataTable = useMetadataTable(dataSourceName, table);
const nonSelectedQueryTypePermissions = getNonSelectedQueryTypePermissions(
metadataTable?.data as MetadataTable,
queryType,
roleName
);
const { watch, setValue, reset, getValues } = useFormContext();
// determines whether the inputs should be pointed at `check` or `filter`
const rowPermissions = getRowPermission(queryType, subQueryType);
// determines whether the check type should be pointer at `checkType` or `filterType`
@ -159,11 +171,11 @@ export const RowPermissionsSection: React.FC<RowPermissionsProps> = ({
id={SelectedSection.NoChecks}
type="radio"
value={SelectedSection.NoChecks}
checked={selectedSection === SelectedSection.NoChecks}
onClick={() => {
setValue(rowPermissionsCheckType, SelectedSection.NoChecks);
setValue(rowPermissions, {});
}}
{...register(rowPermissionsCheckType)}
/>
<NoChecksLabel />
</label>
@ -189,43 +201,61 @@ export const RowPermissionsSection: React.FC<RowPermissionsProps> = ({
)}
</div>
{allRowChecks?.map(({ queryType: query, value }) => (
<div key={query}>
<label className="flex items-center gap-2">
<input
id={`custom_${query}`}
data-testid={getUpdatePermissionBuilderIdSuffix(
`external-${roleName}-${query}-input`,
subQueryType
)}
type="radio"
value={query}
{...register(rowPermissionsCheckType)}
onClick={() => {
setValue(rowPermissionsCheckType, query);
setValue(rowPermissions, JSON.parse(value));
}}
/>
<span data-test="mutual-check">
With same custom check as&nbsp;<strong>{query}</strong>
</span>
</label>
{nonSelectedQueryTypePermissions &&
nonSelectedQueryTypePermissions?.map(
({ queryType: type, data }: Record<string, any>) => (
<div key={`${type}${queryType}`}>
<label className="flex items-center gap-2">
<input
id={`custom_${type}`}
data-testid={getUpdatePermissionBuilderIdSuffix(
`external-${roleName}-${type}-input`,
subQueryType
)}
type="radio"
value={type}
checked={selectedSection === type}
onClick={() => {
reset({
...getValues(),
...data,
queryType,
});
{selectedSection === query && (
<div className="pt-4">
{!isLoading && tableName ? (
<RowPermissionBuilder
permissionsKey={permissionsKey}
table={table}
dataSourceName={dataSourceName}
setValue(rowPermissionsCheckType, type);
}}
/>
) : (
<>Loading...</>
<span data-test="mutual-check">
With same custom check as&nbsp;<strong>{type}</strong>
</span>
</label>
{selectedSection === type && (
<div
// Permissions are not otherwise stored in plan JSON format in the dom.
// This is a hack to get the JSON into the dom for testing.
data-state={JSON.stringify(data[getRowPermission(type)])}
data-testid="external-check-json-editor"
className="mt-4 p-6 rounded-lg bg-white border border-gray-200 min-h-32 w-full"
>
<AceEditor
mode="json"
minLines={1}
fontSize={14}
height="18px"
width="100%"
theme="github"
name={`${tableName}-json-editor`}
value={JSON.stringify(data[getRowPermission(type)])}
onChange={() => setValue(rowPermissionsCheckType, type)}
editorProps={{ $blockScrolling: true }}
setOptions={{ useWorker: false }}
/>
</div>
)}
</div>
)}
</div>
))}
)
)}
<div>
<label className="flex items-center gap-2">
@ -237,7 +267,7 @@ export const RowPermissionsSection: React.FC<RowPermissionsProps> = ({
subQueryType
)}
value={SelectedSection.Custom}
{...register(rowPermissionsCheckType)}
checked={selectedSection === SelectedSection.Custom}
onClick={() => {
setValue(rowPermissionsCheckType, SelectedSection.Custom);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment

View File

@ -16,10 +16,7 @@ import {
TableEntry,
} from '../../../../../../../metadata/types';
import {
createPermissionsObject,
getRowPermissionsForAllOtherQueriesMatchingSelectedRole,
} from './utils';
import { createPermissionsObject } from './utils';
interface GetMetadataTableArgs {
table: unknown;
@ -68,17 +65,11 @@ export const createDefaultValues = ({
configuration: selectedTable?.configuration,
});
const allRowChecks = getRowPermissionsForAllOtherQueriesMatchingSelectedRole(
queryType,
roleName,
selectedTable
);
const baseDefaultValues: DefaultValues = {
queryType: 'select',
filterType: 'none',
columns: {},
allRowChecks,
supportedOperators,
};
@ -99,6 +90,5 @@ export const createDefaultValues = ({
};
type DefaultValues = PermissionsSchema & {
allRowChecks: { queryType: QueryType; value: string }[];
operators?: Record<string, unknown>;
};

View File

@ -90,60 +90,6 @@ const getColumns = (
}, {});
};
export const getAllRowChecks = (
currentQuery: QueryType,
allChecks: Array<{ queryType: QueryType; value: any }> = []
) => {
return allChecks
.filter(({ queryType }) => queryType !== currentQuery)
.map(({ queryType, value }) => {
if (['insert', 'update'].includes(queryType)) {
return { queryType, value: JSON.stringify(value.check || {}) };
}
return {
queryType,
value: JSON.stringify(value.filter || {}),
};
});
};
export interface UseDefaultValuesArgs {
dataSourceName: string;
table: unknown;
roleName: string;
queryType: QueryType;
}
export const getRowPermissionsForAllOtherQueriesMatchingSelectedRole = (
selectedQuery: QueryType,
selectedRole: string,
table?: TableEntry
) => {
const res = Object.entries(table || {}).reduce<
Array<{ queryType: QueryType; value: any }>
>((acc, [key, value]) => {
const props = { key, value };
// check object key of metadata is a permission
if (isPermission(props)) {
// add each role from each permission to the set
props.value.forEach(permission => {
if (permission.role === selectedRole) {
acc.push({
queryType: keyToPermission[props.key],
value: permission.permission,
});
}
});
}
return acc;
}, []);
return getAllRowChecks(selectedQuery, res);
};
export const createPermission = {
insert: (
permission: InsertPermissionDefinition,

View File

@ -48,7 +48,6 @@ const defaultValuesMockResult: ReturnType<typeof createDefaultValues> = {
Series_title_4: false,
Series_title_5: false,
},
allRowChecks: [],
supportedOperators: [
{ name: 'equals', value: '_eq' },
{ name: 'not equals', value: '_neq' },
@ -83,5 +82,6 @@ const defaultValuesMockResult: ReturnType<typeof createDefaultValues> = {
test('use default values returns values correctly', () => {
const result = createDefaultValues(useFormDataCreateDefaultValuesMock);
expect(result).toEqual(defaultValuesMockResult);
});

View File

@ -47,6 +47,7 @@ export const useFormData = ({
JSON.stringify(table),
roleName,
tableColumns,
queryType,
],
queryFn: async () => {
if (tableColumns.length === 0)
@ -89,7 +90,6 @@ export const useFormData = ({
return { formData, defaultValues };
},
refetchOnWindowFocus: false,
});
};

View File

@ -0,0 +1,98 @@
import { MetadataTable } from '../../../hasura-metadata-types/source/table';
export const partiallyAppliedPermissionsData = {
table: ['Album'],
select_permissions: [
{
role: 'asdf',
permission: {
columns: ['Title'],
filter: { ArtistId: { _eq: 'X-Hasura-User-Id' } },
allow_aggregations: true,
},
},
{
role: 'user',
permission: {
columns: { Title: true },
filter: { AlbumId: { _eq: 'X-Hasura-User-Id' } },
check: { AlbumId: { _eq: 'X-Hasura-User-Id' } },
},
},
],
delete_permissions: [
{ role: 'asdf', permission: { filter: {} } },
{ role: 'testrole', permission: { filter: {} } },
{
role: 'user',
permission: {
filter: { AlbumId: { _eq: 'X-Hasura-User-I' } },
check: { AlbumId: { _eq: 'X-Hasura-User-I' } },
columns: {},
},
},
],
} as MetadataTable;
export const fullyAppliedPermissionsData = {
table: ['Album'],
insert_permissions: [
{
role: 'user',
permission: {
check: { ArtistId: { _eq: 'X-Hasura-User-Id' } },
columns: ['Title'],
},
},
],
select_permissions: [
{
role: 'asdf',
permission: {
columns: ['Title'],
filter: { ArtistId: { _eq: 'X-Hasura-User-Id' } },
allow_aggregations: true,
},
},
{
role: 'user',
permission: {
columns: { AlbumId: true, Title: true },
filter: { ArtistId: { _eq: 'X-Hasura-User-Id' } },
check: { ArtistId: { _eq: 'X-Hasura-User-Id' } },
},
},
],
update_permissions: [
{
role: 'user',
permission: {
columns: { Title: true },
filter: { Title: { _eq: 'X-Hasura-User-Id' } },
check: { Title: { _eq: 'X-Hasura-User-Id' } },
},
},
],
delete_permissions: [
{ role: 'asdf', permission: { filter: {} } },
{ role: 'testrole', permission: { filter: {} } },
{ role: 'user', permission: { filter: {}, check: {}, columns: {} } },
],
} as MetadataTable;
export const noAppliedPermissionsData = {
table: ['Album'],
select_permissions: [
{
role: 'asdf',
permission: {
columns: ['Title'],
filter: { ArtistId: { _eq: 'X-Hasura-User-Id' } },
allow_aggregations: true,
},
},
],
delete_permissions: [
{ role: 'asdf', permission: { filter: {} } },
{ role: 'testrole', permission: { filter: {} } },
],
} as MetadataTable;

View File

@ -0,0 +1,71 @@
import { getNonSelectedQueryTypePermissions } from './getMapQueryTypePermissions';
import {
partiallyAppliedPermissionsData,
fullyAppliedPermissionsData,
noAppliedPermissionsData,
} from './getMapQueryTypePermissions.mocks';
describe('getMapQueryTypePermissions should', () => {
test('return existing permissions for table', () => {
const result = getNonSelectedQueryTypePermissions(
partiallyAppliedPermissionsData,
'insert',
'user'
);
expect(result).toEqual([
{
queryType: 'select',
data: {
columns: { Title: true },
filter: { AlbumId: { _eq: 'X-Hasura-User-Id' } },
check: { AlbumId: { _eq: 'X-Hasura-User-Id' } },
},
},
{
queryType: 'delete',
data: {
filter: { AlbumId: { _eq: 'X-Hasura-User-I' } },
check: { AlbumId: { _eq: 'X-Hasura-User-I' } },
columns: {},
},
},
]);
});
test('returns all existing permission except the current one', () => {
const result = getNonSelectedQueryTypePermissions(
fullyAppliedPermissionsData,
'insert',
'user'
);
expect(result).toEqual([
{
queryType: 'select',
data: {
columns: { AlbumId: true, Title: true },
filter: { ArtistId: { _eq: 'X-Hasura-User-Id' } },
check: { ArtistId: { _eq: 'X-Hasura-User-Id' } },
},
},
{
queryType: 'update',
data: {
columns: { Title: true },
filter: { Title: { _eq: 'X-Hasura-User-Id' } },
check: { Title: { _eq: 'X-Hasura-User-Id' } },
},
},
{ queryType: 'delete', data: { filter: {}, check: {}, columns: {} } },
]);
});
test('return empty array when no permissions have been applied for table', () => {
const result = getNonSelectedQueryTypePermissions(
noAppliedPermissionsData,
'insert',
'user'
);
expect(result).toEqual([]);
});
});

View File

@ -0,0 +1,118 @@
import { MetadataTable } from '../../../hasura-metadata-types/source/table';
const formatColumns = (columns: string[] | Record<string, boolean>) => {
if (!Array.isArray(columns)) return columns || {};
return (
columns?.reduce((tally, column) => {
return { ...tally, [column]: true };
}, {}) || {}
);
};
const getPermissionsMappedByRole = ({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType,
}: {
tableData: MetadataTable;
key:
| 'delete_permissions'
| 'insert_permissions'
| 'select_permissions'
| 'update_permissions';
currentRole: string;
currentQueryType: string;
tally: Record<string, any>[];
queryType: string;
}) => {
const permissions =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
tableData?.[key]?.find(
(data: { role: string }) => data.role === currentRole
)?.permission;
if (!permissions) return tally;
if (currentQueryType === 'update' || currentQueryType === 'insert') {
permissions.check = permissions?.filter;
} else if (currentQueryType === 'select' || currentQueryType === 'delete') {
permissions.filter = permissions?.check;
}
permissions.columns = formatColumns(permissions.columns);
return [
...tally,
{
queryType,
data: permissions,
},
];
};
export const getNonSelectedQueryTypePermissions = (
tableData: MetadataTable,
currentQueryType: string,
currentRole: string
) => {
if (!tableData) return [];
const metadataKeys = Object.keys(tableData);
const existingPermissions = metadataKeys
?.filter(
key =>
key === 'update_permissions' ||
key === 'select_permissions' ||
key === 'delete_permissions' ||
key === 'insert_permissions'
)
?.reduce((tally: Record<string, any>[], key: string) => {
if (key === 'select_permissions' && currentQueryType !== 'select') {
return getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType: 'select',
});
}
if (key === 'update_permissions' && currentQueryType !== 'update') {
return getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType: 'update',
});
}
if (key === 'delete_permissions' && currentQueryType !== 'delete') {
return getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType: 'delete',
});
}
if (key === 'insert_permissions' && currentQueryType !== 'insert') {
return getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType: 'insert',
});
}
return tally;
}, []);
return existingPermissions;
};

View File

@ -50,34 +50,30 @@ GDCUpdateTableCloneSelectPermission.parameters = {
GDCUpdateTableCloneSelectPermission.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
await waitFor(
async () => await canvas.getByTestId('permission-table-button-user-update')
);
await canvas.findByTestId('permission-table-button-user-update');
const tableUserUpdateButton = await canvas.getByTestId(
'permission-table-button-user-update'
);
await userEvent.click(tableUserUpdateButton);
await waitFor(
async () => await canvas.getByTestId('external-user-select-input-pre')
);
const selectCheckbox = await canvas.getByTestId(
`external-user-select-input-pre`
const selectCheckbox = await canvas.findByTestId(
'external-user-select-input-pre'
);
await userEvent.click(selectCheckbox);
await waitFor(async () => await canvas.getByTestId('row-permission-builder'));
await canvas.findByTestId('external-check-json-editor');
const rowPermissionBuilderContainer = await canvas.getByTestId(
`row-permission-builder`
'external-check-json-editor'
);
expect(rowPermissionBuilderContainer.getAttribute('data-state')).toEqual(
'{"ArtistId":{"_eq":"X-Hasura-User-Id"}}'
);
};
export const GDCUpdateTableCreatePermissions: Story<
PermissionsTabProps
> = args => <PermissionsTab {...args} />;
// export const GDCUpdateTableCreatePermissions: Story<
// PermissionsTabProps
// > = args => <PermissionsTab {...args} />;
// GDCUpdateTableCreatePermissions.args = {
// dataSourceName: 'sqlite',

View File

@ -127,8 +127,10 @@ export const PermissionsTable: React.FC<PermissionsTableProps> = ({
{permissionTypes.map(({ permissionType, access }) => {
// TODO: add checks to see what permissions are supported by each db
const isEditable =
driverSupportedQueries.includes(permissionType);
driverSupportedQueries.includes(permissionType) ||
roleName !== 'admin';
if (isNewRole) {
return (