mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 17:31:56 +03:00
console: add clone permissions to GDC permissions tab
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7620 Co-authored-by: Julian <843342+okjulian@users.noreply.github.com> GitOrigin-RevId: 0e94fe8f6dc4233233a3c038c0d48a3f6c4dab50
This commit is contained in:
parent
a5578cb4bd
commit
4062d5f515
@ -39,5 +39,8 @@ export const alloy: Database = {
|
||||
const { name, schema } = table as AlloyDbTable;
|
||||
return schema === 'public' ? name : `${schema}_${name}`;
|
||||
},
|
||||
getSupportedQueryTypes: async () => {
|
||||
return ['select', 'insert', 'update', 'delete'];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -39,5 +39,8 @@ export const bigquery: Database = {
|
||||
const { name, dataset } = table as BigQueryTable;
|
||||
return `${dataset}_${name}`;
|
||||
},
|
||||
getSupportedQueryTypes: async () => {
|
||||
return ['select'];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -74,5 +74,8 @@ export const citus: Database = {
|
||||
const { name, schema } = table as CitusTable;
|
||||
return schema === 'public' ? name : `${schema}_${name}`;
|
||||
},
|
||||
getSupportedQueryTypes: async () => {
|
||||
return ['select', 'insert', 'update', 'delete'];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -72,5 +72,8 @@ export const cockroach: Database = {
|
||||
const { name, schema } = table as CockroachDBTable;
|
||||
return schema === 'public' ? name : `${schema}_${name}`;
|
||||
},
|
||||
getSupportedQueryTypes: async () => {
|
||||
return ['select', 'insert', 'update', 'delete'];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -3,6 +3,7 @@ import { Database, Feature } from '..';
|
||||
export const defaultDatabaseProps: Database = {
|
||||
config: {
|
||||
getDefaultQueryRoot: async () => Feature.NotImplemented,
|
||||
getSupportedQueryTypes: async () => Feature.NotImplemented,
|
||||
},
|
||||
introspection: {
|
||||
getDriverInfo: async () => Feature.NotImplemented,
|
||||
|
@ -42,5 +42,8 @@ export const gdc: Database = {
|
||||
getDefaultQueryRoot: async (table: Table) => {
|
||||
return (table as GDCTable).join('_');
|
||||
},
|
||||
getSupportedQueryTypes: async () => {
|
||||
return ['select'];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -47,6 +47,7 @@ import {
|
||||
} from './api';
|
||||
import { getAllSourceKinds } from './common/getAllSourceKinds';
|
||||
import { getTableName } from './common/getTableName';
|
||||
import { QueryType } from '../Permissions/types';
|
||||
|
||||
export enum Feature {
|
||||
NotImplemented = 'Not Implemented',
|
||||
@ -110,6 +111,9 @@ export type Database = {
|
||||
getDefaultQueryRoot: (
|
||||
table: Table
|
||||
) => Promise<string | Feature.NotImplemented>;
|
||||
getSupportedQueryTypes: (
|
||||
table: Table
|
||||
) => Promise<QueryType[] | Feature.NotImplemented>;
|
||||
};
|
||||
};
|
||||
|
||||
@ -431,11 +435,18 @@ export const DataSource = (httpClient: AxiosInstance) => ({
|
||||
}) => {
|
||||
const database = await getDatabaseMethods({ dataSourceName, httpClient });
|
||||
|
||||
const result = await database.config.getDefaultQueryRoot(table);
|
||||
return database.config.getDefaultQueryRoot(table);
|
||||
},
|
||||
getSupportedQueryTypes: async ({
|
||||
dataSourceName,
|
||||
table,
|
||||
}: {
|
||||
dataSourceName: string;
|
||||
table: Table;
|
||||
}) => {
|
||||
const database = await getDatabaseMethods({ dataSourceName, httpClient });
|
||||
|
||||
if (result === Feature.NotImplemented) return Feature.NotImplemented;
|
||||
|
||||
return result;
|
||||
return database.config.getSupportedQueryTypes(table);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -63,5 +63,8 @@ export const mssql: Database = {
|
||||
const { name, schema } = table as MssqlTable;
|
||||
return schema === 'dbo' ? name : `${schema}_${name}`;
|
||||
},
|
||||
getSupportedQueryTypes: async () => {
|
||||
return ['select', 'insert', 'update', 'delete'];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -22,4 +22,8 @@ export const mysql: Database = {
|
||||
query: {
|
||||
getTableRows: async () => Feature.NotImplemented,
|
||||
},
|
||||
config: {
|
||||
getDefaultQueryRoot: async () => Feature.NotImplemented,
|
||||
getSupportedQueryTypes: async () => Feature.NotImplemented,
|
||||
},
|
||||
};
|
||||
|
@ -39,5 +39,8 @@ export const postgres: Database = {
|
||||
const { name, schema } = table as PostgresTable;
|
||||
return schema === 'public' ? name : `${schema}_${name}`;
|
||||
},
|
||||
getSupportedQueryTypes: async () => {
|
||||
return ['select', 'insert', 'update', 'delete'];
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -1,6 +1,10 @@
|
||||
import { DataSource, Feature } from '@/features/DataSource';
|
||||
import { DataTarget } from '@/features/Datasources';
|
||||
import { Table } from '@/features/hasura-metadata-types';
|
||||
import { useHttpClient } from '@/features/Network';
|
||||
|
||||
import type { QualifiedTable } from '@/metadata/types';
|
||||
import { useQuery } from 'react-query';
|
||||
import { MetadataSelector } from './metadataSelectors';
|
||||
import { useMetadata } from './useMetadata';
|
||||
|
||||
@ -18,6 +22,26 @@ export const useRemoteDatabaseRelationships = (target: DataTarget) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const useSupportedQueryTypes = ({
|
||||
dataSourceName,
|
||||
table,
|
||||
}: {
|
||||
table: Table;
|
||||
dataSourceName: string;
|
||||
}) => {
|
||||
const httpClient = useHttpClient();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['supported-query-types'],
|
||||
queryFn: async () => {
|
||||
return DataSource(httpClient).getSupportedQueryTypes({
|
||||
dataSourceName,
|
||||
table,
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRemoteSchemaRelationships = (
|
||||
database: string,
|
||||
table: QualifiedTable
|
||||
|
@ -5,6 +5,7 @@ export {
|
||||
useMetadataTables,
|
||||
useRemoteDatabaseRelationships,
|
||||
useRemoteSchemaRelationships,
|
||||
useSupportedQueryTypes,
|
||||
} from './hooks/useMetadataTables';
|
||||
export { useMetadataVersion } from './hooks/useMetadataVersion';
|
||||
export { useMetadataTableComputedFields } from './hooks/useMetadataTableComputedFields';
|
||||
|
@ -2,11 +2,20 @@ import React from 'react';
|
||||
import { useConsoleForm } from '@/new-components/Form';
|
||||
import { Button } from '@/new-components/Button';
|
||||
import { IndicatorCard } from '@/new-components/IndicatorCard';
|
||||
import {
|
||||
MetadataSelector,
|
||||
useMetadata,
|
||||
useRoles,
|
||||
useSupportedQueryTypes,
|
||||
} from '@/features/MetadataAPI';
|
||||
import { getTableDisplayName } from '@/features/DatabaseRelationships';
|
||||
|
||||
import { PermissionsSchema, schema } from './../schema';
|
||||
import { AccessType, QueryType } from '../types';
|
||||
import {
|
||||
AggregationSection,
|
||||
BackendOnlySection,
|
||||
ClonePermissionsSection,
|
||||
ColumnPermissionsSection,
|
||||
ColumnPresetsSection,
|
||||
RowPermissionsSection,
|
||||
@ -37,14 +46,24 @@ const Component = (props: ComponentProps) => {
|
||||
data,
|
||||
} = props;
|
||||
|
||||
const { data: metadataTables } = useMetadata(
|
||||
MetadataSelector.getTables(dataSourceName)
|
||||
);
|
||||
const tables = metadataTables?.map(t => t.table) ?? [];
|
||||
// functions fired when the form is submitted
|
||||
const { updatePermissions, deletePermissions } = useUpdatePermissions({
|
||||
dataSourceName,
|
||||
table,
|
||||
tables,
|
||||
queryType,
|
||||
roleName,
|
||||
accessType,
|
||||
});
|
||||
const { data: roles } = useRoles();
|
||||
const { data: supportedQueryTypes } = useSupportedQueryTypes({
|
||||
dataSourceName,
|
||||
table,
|
||||
});
|
||||
|
||||
const onSubmit = async (formData: PermissionsSchema) => {
|
||||
await updatePermissions.submit(formData);
|
||||
@ -56,9 +75,6 @@ const Component = (props: ComponentProps) => {
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const isSubmittingError =
|
||||
updatePermissions.isError || deletePermissions.isError;
|
||||
//
|
||||
// for update it is possible to set pre update and post update row checks
|
||||
const rowPermissions = queryType === 'update' ? ['pre', 'post'] : [queryType];
|
||||
|
||||
@ -74,12 +90,6 @@ const Component = (props: ComponentProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
if (isSubmittingError) {
|
||||
return (
|
||||
<IndicatorCard status="negative">Error submitting form</IndicatorCard>
|
||||
);
|
||||
}
|
||||
|
||||
// allRowChecks relates to other queries and is for duplicating from others
|
||||
const allRowChecks = defaultValues?.allRowChecks;
|
||||
|
||||
@ -159,14 +169,14 @@ const Component = (props: ComponentProps) => {
|
||||
)}
|
||||
|
||||
<hr className="my-4" />
|
||||
{/* {!!tableNames?.length && (
|
||||
|
||||
<ClonePermissionsSection
|
||||
queryType={queryType}
|
||||
tables={tableNames}
|
||||
supportedQueryTypes={supportedQueries}
|
||||
roles={allRoles}
|
||||
supportedQueryTypes={supportedQueryTypes}
|
||||
tables={tables}
|
||||
roles={roles}
|
||||
/>
|
||||
)} */}
|
||||
|
||||
<div className="pt-2 flex gap-2">
|
||||
<Button
|
||||
type="submit"
|
||||
|
@ -3,10 +3,11 @@ import { allowedMetadataTypes } from '@/features/MetadataAPI';
|
||||
import { AccessType, QueryType } from '../../types';
|
||||
import { PermissionsSchema } from '../../schema';
|
||||
import { createInsertArgs } from './utils';
|
||||
import { Table } from '@/features/hasura-metadata-types';
|
||||
|
||||
interface CreateBodyArgs {
|
||||
dataSourceName: string;
|
||||
table: unknown;
|
||||
table: Table;
|
||||
roleName: string;
|
||||
resourceVersion: number;
|
||||
}
|
||||
@ -105,6 +106,7 @@ interface CreateInsertBodyArgs extends CreateBodyArgs {
|
||||
accessType: AccessType;
|
||||
existingPermissions: any;
|
||||
driver: string;
|
||||
tables: Table[];
|
||||
}
|
||||
|
||||
export interface InsertBodyResult {
|
||||
@ -123,6 +125,7 @@ const createInsertBody = ({
|
||||
resourceVersion,
|
||||
existingPermissions,
|
||||
driver,
|
||||
tables,
|
||||
}: CreateInsertBodyArgs): InsertBodyResult => {
|
||||
const args = createInsertArgs({
|
||||
driver,
|
||||
@ -133,6 +136,7 @@ const createInsertBody = ({
|
||||
formData,
|
||||
accessType,
|
||||
existingPermissions,
|
||||
tables,
|
||||
});
|
||||
|
||||
const formBody = {
|
||||
|
@ -7,6 +7,7 @@ const selectArgs: CreateInsertArgs = {
|
||||
table: 'users',
|
||||
queryType: 'insert',
|
||||
role: 'user',
|
||||
tables: [],
|
||||
formData: {
|
||||
queryType: 'select',
|
||||
filterType: 'none',
|
||||
@ -62,6 +63,7 @@ test('create select args object from form data', () => {
|
||||
allow_aggregations: false,
|
||||
columns: ['email', 'type'],
|
||||
filter: {},
|
||||
presets: [],
|
||||
},
|
||||
role: 'user',
|
||||
source: 'default',
|
||||
|
@ -3,10 +3,14 @@ import produce from 'immer';
|
||||
import { allowedMetadataTypes } from '@/features/MetadataAPI';
|
||||
|
||||
import { AccessType } from '../../types';
|
||||
import { PermissionsSchema } from '../../schema';
|
||||
import { PermissionsSchema, Presets } from '../../schema';
|
||||
import { areTablesEqual } from '@/features/hasura-metadata-api';
|
||||
import { Table } from '@/features/hasura-metadata-types';
|
||||
import { getTableDisplayName } from '@/features/DatabaseRelationships';
|
||||
|
||||
type SelectPermissionMetadata = {
|
||||
columns: string[];
|
||||
presets: Presets;
|
||||
filter: Record<string, any>;
|
||||
allow_aggregations?: boolean;
|
||||
limit?: number;
|
||||
@ -39,6 +43,7 @@ const createSelectObject = (input: PermissionsSchema) => {
|
||||
const permissionObject: SelectPermissionMetadata = {
|
||||
columns,
|
||||
filter,
|
||||
presets: [],
|
||||
allow_aggregations: input.aggregationEnabled,
|
||||
};
|
||||
|
||||
@ -57,7 +62,7 @@ const createSelectObject = (input: PermissionsSchema) => {
|
||||
return permissionObject;
|
||||
}
|
||||
|
||||
return {};
|
||||
throw new Error('Case not handled');
|
||||
};
|
||||
|
||||
/**
|
||||
@ -65,14 +70,14 @@ const createSelectObject = (input: PermissionsSchema) => {
|
||||
*/
|
||||
const createPermission = (formData: PermissionsSchema) => {
|
||||
switch (formData.queryType) {
|
||||
case 'insert':
|
||||
return {};
|
||||
case 'select':
|
||||
return createSelectObject(formData);
|
||||
case 'insert':
|
||||
throw new Error('Case not handled');
|
||||
case 'update':
|
||||
return {};
|
||||
throw new Error('Case not handled');
|
||||
case 'delete':
|
||||
return {};
|
||||
throw new Error('Case not handled');
|
||||
default:
|
||||
throw new Error('Case not handled');
|
||||
}
|
||||
@ -81,6 +86,7 @@ const createPermission = (formData: PermissionsSchema) => {
|
||||
export interface CreateInsertArgs {
|
||||
dataSourceName: string;
|
||||
table: unknown;
|
||||
tables: Table[];
|
||||
queryType: any;
|
||||
role: string;
|
||||
accessType: AccessType;
|
||||
@ -107,6 +113,7 @@ export const createInsertArgs = ({
|
||||
formData,
|
||||
existingPermissions,
|
||||
driver,
|
||||
tables,
|
||||
}: CreateInsertArgs) => {
|
||||
const permission = createPermission(formData);
|
||||
|
||||
@ -127,7 +134,7 @@ export const createInsertArgs = ({
|
||||
// determine if args from form already exist
|
||||
const permissionExists = existingPermissions.find(
|
||||
existingPermission =>
|
||||
JSON.stringify(existingPermission.table) === JSON.stringify(table) &&
|
||||
areTablesEqual(existingPermission.table, table) &&
|
||||
existingPermission.role === role &&
|
||||
existingPermission.queryType === queryType
|
||||
);
|
||||
@ -144,60 +151,59 @@ export const createInsertArgs = ({
|
||||
} as (typeof initialArgs)[0]);
|
||||
}
|
||||
|
||||
// this has been commented out as cloned permissions is not currently used
|
||||
// it's been left in because it could be useful when clone permissions is added back in
|
||||
|
||||
// last item is always empty default
|
||||
// const clonedPermissions = formData?.clonePermissions?.slice(0, -1);
|
||||
const clonedPermissions = formData?.clonePermissions?.slice(0, -1);
|
||||
|
||||
// if (clonedPermissions?.length) {
|
||||
// clonedPermissions.forEach(clonedPermission => {
|
||||
// // if permissions are being applied to a different table
|
||||
// // columns and presets should be blank
|
||||
// const permissionWithColumnsAndPresetsRemoved = produce(
|
||||
// permission,
|
||||
// d => {
|
||||
// if (clonedPermission.tableName !== table) {
|
||||
// d.columns = [];
|
||||
// d.presets = {};
|
||||
// }
|
||||
if (clonedPermissions?.length) {
|
||||
clonedPermissions.forEach(clonedPermission => {
|
||||
const clonedPermissionTable = tables.find(
|
||||
t => getTableDisplayName(t) === clonedPermission.tableName
|
||||
);
|
||||
// if permissions are being applied to a different table
|
||||
// columns and presets should be blank
|
||||
const permissionWithColumnsAndPresetsRemoved = produce(
|
||||
permission,
|
||||
d => {
|
||||
if (!areTablesEqual(clonedPermissionTable, table)) {
|
||||
d.columns = [];
|
||||
d.presets = [];
|
||||
}
|
||||
|
||||
// return d;
|
||||
// }
|
||||
// );
|
||||
// // add each closed permission to args
|
||||
// draft.push({
|
||||
// type: `${driver}_create_${clonedPermission.queryType}_permission` as allowedMetadataTypes,
|
||||
// args: {
|
||||
// table: clonedPermission.tableName || '',
|
||||
// role: clonedPermission.roleName || '',
|
||||
// permission: permissionWithColumnsAndPresetsRemoved,
|
||||
// source: dataSourceName,
|
||||
// },
|
||||
// });
|
||||
return d;
|
||||
}
|
||||
);
|
||||
// add each closed permission to args
|
||||
draft.push({
|
||||
type: `${driver}_create_${clonedPermission.queryType}_permission` as allowedMetadataTypes,
|
||||
args: {
|
||||
table: clonedPermissionTable || '',
|
||||
role: clonedPermission.roleName || '',
|
||||
permission: permissionWithColumnsAndPresetsRemoved,
|
||||
source: dataSourceName,
|
||||
},
|
||||
});
|
||||
|
||||
// // determined if the cloned permission already exists
|
||||
// const clonedPermissionExists = existingPermissions.find(
|
||||
// existingPermission =>
|
||||
// JSON.stringify(existingPermission.table) ===
|
||||
// JSON.stringify(clonedPermission.tableName) &&
|
||||
// existingPermission.role === clonedPermission.roleName &&
|
||||
// existingPermission.queryType === clonedPermission.queryType
|
||||
// );
|
||||
// determined if the cloned permission already exists
|
||||
const clonedPermissionExists = existingPermissions.find(
|
||||
existingPermission =>
|
||||
areTablesEqual(existingPermission.table, clonedPermissionTable) &&
|
||||
existingPermission.role === clonedPermission.roleName &&
|
||||
existingPermission.queryType === clonedPermission.queryType
|
||||
);
|
||||
|
||||
// // if it already exists drop it
|
||||
// if (clonedPermissionExists) {
|
||||
// draft.unshift({
|
||||
// type: `${driver}_drop_${clonedPermission.queryType}_permission` as allowedMetadataTypes,
|
||||
// args: {
|
||||
// table: clonedPermission.tableName,
|
||||
// role: clonedPermission.roleName,
|
||||
// source: dataSourceName,
|
||||
// },
|
||||
// } as typeof initialArgs[0]);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// if it already exists drop it
|
||||
if (clonedPermissionExists) {
|
||||
draft.unshift({
|
||||
type: `${driver}_drop_${clonedPermission.queryType}_permission` as allowedMetadataTypes,
|
||||
args: {
|
||||
table: clonedPermissionTable,
|
||||
role: clonedPermission.roleName,
|
||||
source: dataSourceName,
|
||||
},
|
||||
} as (typeof initialArgs)[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return args;
|
||||
|
@ -5,10 +5,14 @@ import { Button } from '@/new-components/Button';
|
||||
import { Collapse } from '@/new-components/deprecated';
|
||||
import { useIsDisabled } from '../hooks/useIsDisabled';
|
||||
import { QueryType } from '../../types';
|
||||
import { Permission } from '../../schema';
|
||||
import { Feature } from '@/features/DataSource';
|
||||
import { Table } from '@/features/hasura-metadata-types';
|
||||
import { getTableDisplayName } from '@/features/DatabaseRelationships';
|
||||
|
||||
interface ClonePermissionsRowProps {
|
||||
id: number;
|
||||
tables: string[];
|
||||
tables: Table[];
|
||||
currentQueryType: QueryType;
|
||||
queryTypes: string[];
|
||||
roleNames: string[];
|
||||
@ -29,7 +33,7 @@ export const ClonePermissionsRow: React.FC<ClonePermissionsRowProps> = ({
|
||||
const { register, watch } = useFormContext();
|
||||
|
||||
const formKey = 'clonePermissions';
|
||||
const watched: ClonePermission = watch(`${formKey}.${id}`);
|
||||
const watched: Permission = watch(`${formKey}.${id}`);
|
||||
|
||||
const allDisabled = useIsDisabled(currentQueryType as QueryType);
|
||||
|
||||
@ -46,11 +50,14 @@ export const ClonePermissionsRow: React.FC<ClonePermissionsRowProps> = ({
|
||||
Table Name
|
||||
</option>
|
||||
|
||||
{tables?.map(tableName => (
|
||||
{tables?.map(table => {
|
||||
const tableName = getTableDisplayName(table);
|
||||
return (
|
||||
<option key={tableName} value={tableName}>
|
||||
{tableName}
|
||||
</option>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -105,20 +112,13 @@ export const ClonePermissionsRow: React.FC<ClonePermissionsRowProps> = ({
|
||||
};
|
||||
|
||||
export interface ClonePermissionsSectionProps {
|
||||
queryType: any;
|
||||
tables: string[];
|
||||
supportedQueryTypes: string[];
|
||||
queryType: string;
|
||||
tables: Table[];
|
||||
supportedQueryTypes: QueryType[] | Feature | undefined;
|
||||
roles: string[];
|
||||
defaultOpen?: boolean;
|
||||
}
|
||||
|
||||
export interface ClonePermission {
|
||||
id: number;
|
||||
tableName: string;
|
||||
queryType: any;
|
||||
roleName: string;
|
||||
}
|
||||
|
||||
export const ClonePermissionsSection: React.FC<
|
||||
ClonePermissionsSectionProps
|
||||
> = ({ queryType, tables, supportedQueryTypes, roles, defaultOpen }) => {
|
||||
@ -131,7 +131,7 @@ export const ClonePermissionsSection: React.FC<
|
||||
name: 'clonePermissions',
|
||||
});
|
||||
|
||||
const watched: ClonePermission[] = watch('clonePermissions');
|
||||
const watched: Permission[] = watch('clonePermissions');
|
||||
const controlledFields = fields.map((field, index) => {
|
||||
return {
|
||||
...field,
|
||||
@ -152,9 +152,14 @@ export const ClonePermissionsSection: React.FC<
|
||||
tableName: '',
|
||||
queryType: '',
|
||||
roleName: '',
|
||||
} as ClonePermission);
|
||||
} as Permission);
|
||||
}
|
||||
}, [controlledFields, append]);
|
||||
const queryTypes =
|
||||
supportedQueryTypes === Feature.NotImplemented ||
|
||||
supportedQueryTypes === undefined
|
||||
? []
|
||||
: supportedQueryTypes;
|
||||
|
||||
return (
|
||||
<Collapse defaultOpen={defaultOpen}>
|
||||
@ -176,7 +181,7 @@ export const ClonePermissionsSection: React.FC<
|
||||
id={index}
|
||||
tables={tables}
|
||||
currentQueryType={queryType as QueryType}
|
||||
queryTypes={supportedQueryTypes}
|
||||
queryTypes={queryTypes}
|
||||
roleNames={roles}
|
||||
remove={() => remove(index)}
|
||||
/>
|
||||
|
@ -71,7 +71,6 @@ export const ColumnRootFieldPermissions: React.FC<
|
||||
'query_root_fields',
|
||||
'subscription_root_fields',
|
||||
]);
|
||||
console.log('table', table);
|
||||
const disabled = filterType === 'none';
|
||||
const { columns: tableColumns } = useListAllTableColumns(
|
||||
dataSourceName,
|
||||
@ -156,10 +155,22 @@ export const ColumnRootFieldPermissions: React.FC<
|
||||
<FaQuestionCircle aria-hidden="true" />
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
|
||||
<OverlayTrigger
|
||||
placement="right"
|
||||
overlay={
|
||||
<Tooltip tooltipContentChildren>
|
||||
By enabling this you can customize the root field permissions.
|
||||
When this switch is turned off, all values are enabled by
|
||||
default.
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<FaQuestionCircle aria-hidden="true" />
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
<div
|
||||
className={`px-md ${clsx(
|
||||
!isRootPermissionsSwitchedOn && 'hidden'
|
||||
)}`}
|
||||
className={`px-md ${clsx(!isRootPermissionsSwitchedOn && 'hidden')}`}
|
||||
>
|
||||
<SelectPermissionsRow
|
||||
currentPermissions={queryRootFields}
|
||||
@ -169,9 +180,7 @@ export const ColumnRootFieldPermissions: React.FC<
|
||||
isSubscriptionStreamingEnabled={isSubscriptionStreamingEnabled}
|
||||
permissionFields={queryRootPermissionFields}
|
||||
permissionType={QUERY_ROOT_VALUES}
|
||||
onToggleAll={() =>
|
||||
onToggleAll(QUERY_ROOT_VALUES, queryRootFields)
|
||||
}
|
||||
onToggleAll={() => onToggleAll(QUERY_ROOT_VALUES, queryRootFields)}
|
||||
onUpdate={onUpdatePermission}
|
||||
/>
|
||||
<SelectPermissionsRow
|
||||
@ -190,7 +199,6 @@ export const ColumnRootFieldPermissions: React.FC<
|
||||
onUpdate={onUpdatePermission}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Collapse.Content>
|
||||
</Collapse>
|
||||
);
|
||||
|
@ -31,14 +31,21 @@ export const createOperatorsObject = ({
|
||||
name: key,
|
||||
typeName: key,
|
||||
type: 'boolOperator',
|
||||
[key]: value.map((each: Record<string, any>) =>
|
||||
[key]: Array.isArray(value)
|
||||
? value.map((each: Record<string, any>) =>
|
||||
createOperatorsObject({
|
||||
tableName,
|
||||
schema,
|
||||
existingPermission: each,
|
||||
tableConfig,
|
||||
})
|
||||
),
|
||||
)
|
||||
: createOperatorsObject({
|
||||
tableName,
|
||||
schema,
|
||||
existingPermission: value,
|
||||
tableConfig,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,12 @@ import { AccessType, QueryType } from '../../../types';
|
||||
import { api } from '../../api';
|
||||
import { isPermission, keyToPermission } from '../../../utils';
|
||||
import { PermissionsSchema } from '../../../schema';
|
||||
import { Table } from '@/features/hasura-metadata-types';
|
||||
|
||||
export interface UseSubmitFormArgs {
|
||||
dataSourceName: string;
|
||||
table: unknown;
|
||||
table: Table;
|
||||
tables: Table[];
|
||||
roleName: string;
|
||||
queryType: QueryType;
|
||||
accessType: AccessType;
|
||||
@ -21,7 +23,7 @@ export interface UseSubmitFormArgs {
|
||||
interface ExistingPermissions {
|
||||
role: string;
|
||||
queryType: QueryType;
|
||||
table: unknown;
|
||||
table: Table;
|
||||
}
|
||||
|
||||
interface GetAllPermissionsArgs {
|
||||
@ -62,7 +64,8 @@ const getAllPermissions = async ({
|
||||
};
|
||||
|
||||
export const useSubmitForm = (args: UseSubmitFormArgs) => {
|
||||
const { dataSourceName, table, roleName, queryType, accessType } = args;
|
||||
const { dataSourceName, table, roleName, queryType, accessType, tables } =
|
||||
args;
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const httpClient = useHttpClient();
|
||||
@ -92,6 +95,7 @@ export const useSubmitForm = (args: UseSubmitFormArgs) => {
|
||||
dataSourceName,
|
||||
driver: metadataSource.kind,
|
||||
table,
|
||||
tables,
|
||||
roleName,
|
||||
queryType,
|
||||
accessType,
|
||||
|
@ -2,10 +2,12 @@ import { useSubmitForm } from './useSubmitForm';
|
||||
import { useDeletePermission } from './useDeletePermission';
|
||||
|
||||
import { AccessType, QueryType } from '../../../types';
|
||||
import { Table } from '@/features/hasura-metadata-types';
|
||||
|
||||
export interface UseUpdatePermissionsArgs {
|
||||
dataSourceName: string;
|
||||
table: unknown;
|
||||
table: Table;
|
||||
tables: Table[];
|
||||
roleName: string;
|
||||
queryType: QueryType;
|
||||
accessType: AccessType;
|
||||
@ -14,6 +16,7 @@ export interface UseUpdatePermissionsArgs {
|
||||
export const useUpdatePermissions = ({
|
||||
dataSourceName,
|
||||
table,
|
||||
tables,
|
||||
roleName,
|
||||
queryType,
|
||||
accessType,
|
||||
@ -21,6 +24,7 @@ export const useUpdatePermissions = ({
|
||||
const updatePermissions = useSubmitForm({
|
||||
dataSourceName,
|
||||
table,
|
||||
tables,
|
||||
roleName,
|
||||
queryType,
|
||||
accessType,
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './PermissionsTab';
|
||||
export * from './types';
|
||||
|
@ -11,26 +11,46 @@ const presets = z.optional(
|
||||
)
|
||||
);
|
||||
|
||||
export type Presets = z.infer<typeof presets>;
|
||||
|
||||
export type PermissionsSchema = z.infer<typeof schema>;
|
||||
|
||||
const queryType = z.union([
|
||||
z.literal(''),
|
||||
z.literal('insert'),
|
||||
z.literal('select'),
|
||||
z.literal('update'),
|
||||
z.literal('delete'),
|
||||
]);
|
||||
|
||||
export type Permission = z.infer<typeof permission>;
|
||||
|
||||
const permission = z.object({
|
||||
tableName: z.string(),
|
||||
queryType,
|
||||
roleName: z.string(),
|
||||
});
|
||||
|
||||
export const schema = z.discriminatedUnion('queryType', [
|
||||
z.object({
|
||||
queryType: z.literal('insert'),
|
||||
checkType: z.string(),
|
||||
filterType: z.string(),
|
||||
check: z.any(),
|
||||
columns,
|
||||
presets,
|
||||
backendOnly: z.boolean().optional(),
|
||||
clonePermissions: z.array(z.any()).optional(),
|
||||
clonePermissions: z.array(permission).optional(),
|
||||
}),
|
||||
z.object({
|
||||
queryType: z.literal('select'),
|
||||
filterType: z.string(),
|
||||
filter: z.any(),
|
||||
columns,
|
||||
presets,
|
||||
rowCount: z.string().optional(),
|
||||
aggregationEnabled: z.boolean().optional(),
|
||||
clonePermissions: z.array(z.any()).optional(),
|
||||
clonePermissions: z.array(permission).optional(),
|
||||
query_root_fields: z.array(z.string()).nullable().optional(),
|
||||
subscription_root_fields: z.array(z.string()).nullable().optional(),
|
||||
}),
|
||||
@ -43,14 +63,14 @@ export const schema = z.discriminatedUnion('queryType', [
|
||||
check: z.any(),
|
||||
presets,
|
||||
backendOnly: z.boolean().optional(),
|
||||
clonePermissions: z.array(z.any()).optional(),
|
||||
clonePermissions: z.array(permission).optional(),
|
||||
}),
|
||||
z.object({
|
||||
queryType: z.literal('delete'),
|
||||
filterType: z.string(),
|
||||
filter: z.any(),
|
||||
backendOnly: z.boolean().optional(),
|
||||
clonePermissions: z.array(z.any()).optional(),
|
||||
clonePermissions: z.array(permission).optional(),
|
||||
}),
|
||||
]);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user