console: Separate update into pre_update and post_update

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8918
GitOrigin-RevId: b7e066e951158f5ce1dafb062aef5cc1332d617f
This commit is contained in:
Erik Magnusson 2023-05-08 15:53:57 +02:00 committed by hasura-bot
parent f4f1c9f0df
commit 48341c1ff9
9 changed files with 148 additions and 83 deletions

View File

@ -140,7 +140,7 @@ const Component = (props: ComponentProps) => {
table={table}
roleName={roleName}
queryType={queryType}
subQueryType={queryType === 'update' ? 'pre' : undefined}
subQueryType={queryType === 'update' ? 'pre_update' : undefined}
permissionsKey={filterKeys[0]}
dataSourceName={dataSourceName}
supportedOperators={data?.defaultValues?.supportedOperators ?? []}
@ -157,7 +157,9 @@ const Component = (props: ComponentProps) => {
table={table}
roleName={roleName}
queryType={queryType}
subQueryType={queryType === 'update' ? 'post' : undefined}
subQueryType={
queryType === 'update' ? 'post_update' : undefined
}
permissionsKey={filterKeys[1]}
dataSourceName={dataSourceName}
supportedOperators={

View File

@ -20,7 +20,7 @@ test('create select args object from form data', () => {
permission: {
columns: ['AlbumId', 'Title', 'ArtistId'],
filter: { _not: { AlbumId: { _eq: 'X-Hasura-User-Id' } } },
set: [],
set: {},
allow_aggregations: false,
},
source: 'Chinook',

View File

@ -48,7 +48,7 @@ const createSelectObject = (input: PermissionsSchema) => {
const permissionObject: SelectPermissionMetadata = {
columns,
filter,
set: [],
set: {},
allow_aggregations: input.aggregationEnabled,
};

View File

@ -16,6 +16,7 @@ import { QueryType } from '../../types';
import { ReturnValue } from '../hooks';
import { useMetadataTable } from '../../../hasura-metadata-api/metadataHooks';
import { getNonSelectedQueryTypePermissions } from '../utils/getMapQueryTypePermissions';
import { copyQueryTypePermissions } from '../utils/copyQueryTypePermissions';
const NoChecksLabel = () => (
<span data-test="without-checks">Without any checks&nbsp;</span>
@ -31,7 +32,7 @@ const CustomLabel = () => (
export interface RowPermissionsProps {
table: unknown;
queryType: QueryType;
subQueryType?: string;
subQueryType?: 'pre_update' | 'post_update';
dataSourceName: string;
supportedOperators: Operator[];
defaultValues: ReturnValue['defaultValues'];
@ -49,39 +50,32 @@ enum SelectedSection {
delete = 'delete',
}
const getRowPermission = (queryType: QueryType, subQueryType?: string) => {
if (queryType === 'insert') {
export type RowPermissionsSectionType =
| QueryType
| 'pre_update'
| 'post_update';
export const getRowPermission = (
queryType: RowPermissionsSectionType
): 'filter' | 'check' => {
if (queryType === 'pre_update') {
return 'filter';
}
if (queryType === 'post_update') {
return 'check';
}
if (queryType === 'update') {
if (subQueryType === 'post') {
return 'check';
}
return 'filter';
if (queryType === 'insert') {
return 'check';
}
return 'filter';
};
const getRowPermissionCheckType = (
queryType: QueryType,
subQueryType?: string
) => {
if (queryType === 'insert') {
return 'checkType';
}
if (queryType === 'update') {
if (subQueryType === 'post') {
return 'checkType';
}
return 'filterType';
}
return 'filterType';
queryType: RowPermissionsSectionType
): 'filterType' | 'checkType' => {
const permissionType = getRowPermission(queryType);
return permissionType === 'filter' ? 'filterType' : 'checkType';
};
const useTypeName = ({
@ -153,16 +147,17 @@ export const RowPermissionsSection: React.FC<RowPermissionsProps> = ({
roleName
);
const { watch, setValue, reset, getValues } = useFormContext();
const { watch, setValue } = useFormContext();
// determines whether the inputs should be pointed at `check` or `filter`
const rowPermissions = getRowPermission(queryType, subQueryType);
const rowPermissions = getRowPermission(subQueryType ?? queryType);
// determines whether the check type should be pointer at `checkType` or `filterType`
const rowPermissionsCheckType = getRowPermissionCheckType(
queryType,
subQueryType
subQueryType ?? queryType
);
const selectedSection = watch(rowPermissionsCheckType);
return (
<fieldset key={queryType} className="grid gap-2">
<div>
@ -216,13 +211,14 @@ export const RowPermissionsSection: React.FC<RowPermissionsProps> = ({
value={type}
checked={selectedSection === type}
onClick={() => {
reset({
...getValues(),
...data,
queryType,
});
setValue(rowPermissionsCheckType, type);
const newValues = copyQueryTypePermissions(
type,
queryType,
subQueryType,
data
);
setValue(...newValues);
}}
/>
<span data-test="mutual-check">
@ -234,7 +230,9 @@ export const RowPermissionsSection: React.FC<RowPermissionsProps> = ({
<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-state={JSON.stringify(
getRowPermission(type) ? 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"
>

View File

@ -0,0 +1,19 @@
import { copyQueryTypePermissions } from './copyQueryTypePermissions';
describe('copyQueryTypePermissions should', () => {
test('handle pre_update mapping', () => {
const result = copyQueryTypePermissions('insert', 'update', 'pre_update', {
check: { Name: { _eq: 'select' } },
columns: {},
});
expect(result).toEqual(['filter', { Name: { _eq: 'select' } }]);
});
test('handle pre_update mapping', () => {
const result = copyQueryTypePermissions('insert', 'update', 'post_update', {
check: { Name: { _eq: 'insert' } },
columns: {},
});
expect(result).toEqual(['check', { Name: { _eq: 'insert' } }]);
});
});

View File

@ -0,0 +1,22 @@
import {
RowPermissionsSectionType,
getRowPermission,
} from '../components/RowPermissions';
export const copyQueryTypePermissions = (
permissoinType: RowPermissionsSectionType,
queryType: RowPermissionsSectionType,
subQueryType: 'pre_update' | 'post_update' | undefined,
data: Record<string, any>
): [string, Record<string, string>] => {
const mappedType = getRowPermission(permissoinType);
if (subQueryType) {
// This only applies to the visual section of 'pre_update' and 'post_update'
const mappedsubQueryType = getRowPermission(subQueryType);
return [mappedsubQueryType, data[mappedType]];
}
// This only applies to the real query types 'select', 'insert', 'update', 'delete'
// It has to take the special cases of 'pre_update' and 'post_update' into account when mapping values from the update permissions
const mappedQueryType = getRowPermission(queryType);
return [mappedQueryType, data[mappedType]];
};

View File

@ -49,7 +49,15 @@ describe('getMapQueryTypePermissions should', () => {
},
},
{
queryType: 'update',
queryType: 'pre_update',
data: {
columns: { Title: true },
filter: { Title: { _eq: 'X-Hasura-User-Id' } },
check: { Title: { _eq: 'X-Hasura-User-Id' } },
},
},
{
queryType: 'post_update',
data: {
columns: { Title: true },
filter: { Title: { _eq: 'X-Hasura-User-Id' } },

View File

@ -14,7 +14,6 @@ const getPermissionsMappedByRole = ({
key,
currentRole,
currentQueryType,
tally,
queryType,
}: {
tableData: MetadataTable;
@ -25,7 +24,6 @@ const getPermissionsMappedByRole = ({
| 'update_permissions';
currentRole: string;
currentQueryType: string;
tally: Record<string, any>[];
queryType: string;
}) => {
const permissions =
@ -34,21 +32,24 @@ const getPermissionsMappedByRole = ({
tableData?.[key]?.find(
(data: { role: string }) => data.role === currentRole
)?.permission;
if (!permissions) return tally;
if (currentQueryType === 'update' || currentQueryType === 'insert') {
if (!permissions) return null;
if (currentQueryType === 'pre_update') {
if (!permissions.check) permissions.check = permissions?.filter;
}
if (currentQueryType === 'post_update') {
if (!permissions.filter) permissions.filter = permissions?.check;
}
if (currentQueryType === 'insert') {
if (!permissions.check) permissions.check = permissions?.filter;
} else if (currentQueryType === 'select' || currentQueryType === 'delete') {
if (!permissions.filter) permissions.filter = permissions?.check;
}
permissions.columns = formatColumns(permissions.columns);
return [
...tally,
{
queryType,
data: permissions,
},
];
return {
queryType,
data: permissions,
};
};
export const getNonSelectedQueryTypePermissions = (
@ -67,49 +68,64 @@ export const getNonSelectedQueryTypePermissions = (
key === 'delete_permissions' ||
key === 'insert_permissions'
)
?.reduce((tally: Record<string, any>[], key: string) => {
?.reduce((tally: any[], key: string) => {
if (key === 'select_permissions' && currentQueryType !== 'select') {
return getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType: 'select',
});
return [
...tally,
getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
queryType: 'select',
}),
].filter(Boolean);
}
if (key === 'update_permissions' && currentQueryType !== 'update') {
return getPermissionsMappedByRole({
const pre = getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType: 'update',
currentQueryType: 'pre_update',
queryType: 'pre_update',
});
const post = getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType: 'post_update',
queryType: 'post_update',
});
return [...tally, pre, post].filter(Boolean);
}
if (key === 'delete_permissions' && currentQueryType !== 'delete') {
return getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType: 'delete',
});
return [
...tally,
getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
queryType: 'delete',
}),
].filter(Boolean);
}
if (key === 'insert_permissions' && currentQueryType !== 'insert') {
return getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
tally,
queryType: 'insert',
});
return [
...tally,
getPermissionsMappedByRole({
tableData,
key,
currentRole,
currentQueryType,
queryType: 'insert',
}),
].filter(Boolean);
}
return tally;
}, []);

View File

@ -57,7 +57,7 @@ GDCUpdateTableCloneSelectPermission.play = async ({ canvasElement }) => {
await userEvent.click(tableUserUpdateButton);
const selectCheckbox = await canvas.findByTestId(
'external-user-select-input-pre'
'external-user-select-input-pre_update'
);
await userEvent.click(selectCheckbox);