console: Support computed fields in new permissions UI DSF-426

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10493
GitOrigin-RevId: 6c326ccc2ebb636feb8f1fa73eb6f20b0757832b
This commit is contained in:
Julian 2023-11-22 11:21:28 -03:00 committed by hasura-bot
parent 73e413e1ee
commit 1b7f2451ca
20 changed files with 208 additions and 11 deletions

View File

@ -4,7 +4,7 @@ import { Button } from '../../../new-components/Button';
import { IndicatorCard } from '../../../new-components/IndicatorCard';
import {
MetadataSelector,
useMetadata,
useMetadata as useLegacyMetadata,
useRoles,
useSupportedQueryTypes,
} from '../../MetadataAPI';
@ -41,6 +41,7 @@ import {
inputValidationEnabledSchema,
} from '../../../components/Services/Data/TablePermissions/InputValidation/InputValidation';
import { z } from 'zod';
import { MetadataSelectors, useMetadata } from '../../hasura-metadata-api';
export interface ComponentProps {
dataSourceName: string;
@ -70,7 +71,7 @@ const Component = (props: ComponentProps) => {
useScrollIntoView(permissionSectionRef, [roleName], { behavior: 'smooth' });
const { data: metadataTables } = useMetadata(
const { data: metadataTables } = useLegacyMetadata(
MetadataSelector.getTables(dataSourceName)
);
const tables = metadataTables?.map(t => t.table) ?? [];
@ -197,6 +198,7 @@ const Component = (props: ComponentProps) => {
roleName={roleName}
queryType={queryType}
columns={formData?.columns}
computedFields={formData?.computed_fields}
table={table}
dataSourceName={dataSourceName}
/>
@ -281,6 +283,11 @@ export const PermissionsForm = (props: PermissionsFormProps) => {
const { columns: tableColumns, isLoading: isLoadingTables } =
useListAllTableColumns(dataSourceName, table);
const metadataTableResult = useMetadata(
MetadataSelectors.findTable(dataSourceName, table)
);
const computedFields = metadataTableResult.data?.computed_fields ?? [];
const { data: metadataSource } = useMetadataSource(dataSourceName);
const { data, isError, isLoading } = useFormData({
@ -328,6 +335,7 @@ export const PermissionsForm = (props: PermissionsFormProps) => {
metadata: data?.metadata,
table,
tableColumns,
tableComputedFields: computedFields,
defaultQueryRoot: data.defaultQueryRoot,
metadataSource,
supportedOperators: data.supportedOperators,
@ -357,6 +365,7 @@ export const PermissionsForm = (props: PermissionsFormProps) => {
table,
metadata: data.metadata,
tableColumns,
computedFields,
trackedTables: metadataSource.tables,
metadataSource,
validateInput: {

View File

@ -17,11 +17,13 @@ test('create select args object from form data', () => {
args: {
table: ['Album'],
role: 'user',
comment: '',
permission: {
columns: ['AlbumId', 'Title', 'ArtistId'],
filter: { _not: { AlbumId: { _eq: 'X-Hasura-User-Id' } } },
set: {},
allow_aggregations: false,
computed_fields: [],
},
source: 'Chinook',
},
@ -42,6 +44,7 @@ test('create delete args object from form data', () => {
args: {
table: ['Album'],
role: 'user',
comment: '',
permission: { backend_only: false, filter: { Title: { _eq: 'Test' } } },
source: 'Chinook',
},
@ -58,6 +61,7 @@ test('create insert args object from form data', () => {
args: {
table: ['Album'],
role: 'user',
comment: '',
permission: {
columns: [],
check: {
@ -69,6 +73,7 @@ test('create insert args object from form data', () => {
},
allow_upsert: true,
set: {},
validate_input: undefined,
backend_only: false,
},
source: 'Chinook',

View File

@ -30,6 +30,7 @@ const formatFilterValues = (formFilter: Record<string, any>[] = []) => {
type SelectPermissionMetadata = {
columns: string[];
computed_fields: string[];
set: Record<string, any>;
filter: Record<string, any>;
allow_aggregations?: boolean;
@ -43,12 +44,16 @@ const createSelectObject = (input: PermissionsSchema) => {
const columns = Object.entries(input.columns)
.filter(({ 1: value }) => value)
.map(([key]) => key);
const computed_fields = Object.entries(input.computed_fields)
.filter(({ 1: value }) => value)
.map(([key]) => key);
// Input may be undefined
const filter = formatFilterValues(input.filter);
const permissionObject: SelectPermissionMetadata = {
columns,
computed_fields,
filter,
set: {},
allow_aggregations: input.aggregationEnabled,

View File

@ -18,6 +18,7 @@ import {
SubscriptionRootPermissionType,
QueryRootPermissionType,
} from './RootFieldPermissions/types';
import { MetadataSelectors, useMetadata } from '../../../hasura-metadata-api';
const getAccessText = (queryType: string) => {
if (queryType === 'insert') {
@ -35,6 +36,7 @@ export interface ColumnPermissionsSectionProps {
queryType: QueryType;
roleName: string;
columns?: string[];
computedFields?: string[];
table: unknown;
dataSourceName: string;
}
@ -85,19 +87,30 @@ const checkIfConfirmationIsNeeded = (
);
};
// @todo
// this hasn't been fully implemented, it still needs computed columns adding
export const ColumnPermissionsSection: React.FC<
ColumnPermissionsSectionProps
> = ({ roleName, queryType, columns, table, dataSourceName }) => {
> = ({
roleName,
queryType,
columns,
table,
computedFields,
dataSourceName,
}) => {
const { setValue, watch } = useFormContext();
const [showConfirmation, setShowConfirmationModal] = useState<string | null>(
null
);
watch();
const [selectedColumns, queryRootFields, subscriptionRootFields] = watch([
const [
selectedColumns,
selectedComputedFields,
queryRootFields,
subscriptionRootFields,
] = watch([
'columns',
'computed_fields',
'query_root_fields',
'subscription_root_fields',
]);
@ -112,6 +125,13 @@ export const ColumnPermissionsSection: React.FC<
table
);
const metadataTableResult = useMetadata(
MetadataSelectors.findTable(dataSourceName, table)
);
const tableComputedFields = metadataTableResult.data?.computed_fields?.map(
({ name }) => name
);
const onClick = () => {
columns?.forEach(column => {
const toggleAllOn = status !== 'All columns';
@ -119,6 +139,12 @@ export const ColumnPermissionsSection: React.FC<
// otherwise toggle all off
setValue(`columns.${column}`, toggleAllOn);
});
computedFields?.forEach(field => {
const toggleAllOn = status !== 'All columns';
// if status is not all columns: toggle all on
// otherwise toggle all off
setValue(`computed_fields.${field}`, toggleAllOn);
});
};
if (isError) {
@ -206,6 +232,26 @@ export const ColumnPermissionsSection: React.FC<
<i>{fieldName}</i>
</label>
))}
{queryType === 'select' &&
tableComputedFields?.map(fieldName => (
<label key={fieldName} className="flex gap-2 items-center">
<input
type="checkbox"
title={disabled ? 'Set a row permission first' : ''}
disabled={disabled}
style={{ marginTop: '0px !important' }}
className="rounded shadow-sm border border-gray-300 hover:border-gray-400 focus:ring-yellow-400"
checked={selectedComputedFields[fieldName]}
onChange={() => {
setValue(
`computed_fields.${fieldName}`,
!selectedComputedFields[fieldName]
);
}}
/>
<i>{fieldName}</i>
</label>
))}
<Button
type="button"
size="sm"

View File

@ -21,7 +21,8 @@ export const Operator = ({
rowPermissionsContext
);
const { tables } = useContext(rootTableContext);
const { columns, table, relationships } = useContext(tableContext);
const { columns, table, relationships, computedFields } =
useContext(tableContext);
const { rootLogicalModel } = useContext(logicalModelContext);
const parent = path[path.length - 1];
const operatorLevelId =
@ -73,6 +74,19 @@ export const Operator = ({
))}
</optgroup>
) : null}
{computedFields.length ? (
<optgroup label="Computed fields">
{computedFields.map((field, index) => (
<option
data-type="computedField"
key={'computedField' + index}
value={field.name}
>
{field.name}
</option>
))}
</optgroup>
) : null}
{rootLogicalModel?.fields.length ? (
<optgroup label="Columns">
{rootLogicalModel?.fields.map((field, index) => (

View File

@ -5,6 +5,7 @@ import { rootTableContext } from './RootTableProvider';
import { areTablesEqual } from '../../../../../hasura-metadata-api';
import { fieldsToColumns } from './utils/nestedObjects';
import { rowPermissionsContext } from './RowPermissionsProvider';
import { ComputedField } from '../../../../../../metadata/types';
export const tableContext = createContext<TableContext>({
table: {},
@ -13,6 +14,8 @@ export const tableContext = createContext<TableContext>({
setComparator: () => {},
columns: [],
setColumns: () => {},
computedFields: [],
setComputedFields: () => {},
relationships: [],
setRelationships: () => {},
});
@ -29,6 +32,7 @@ export const TableProvider = ({
const [table, setTableName] = useState<Table>(defaultTable || {});
const [comparator, setComparator] = useState<string | undefined>();
const [columns, setColumns] = useState<Columns>([]);
const [computedFields, setComputedFields] = useState<ComputedField[]>([]);
const [relationships, setRelationships] = useState<Relationships>([]);
const { tables, rootTable } = useContext(rootTableContext);
const { loadRelationships } = useContext(rowPermissionsContext);
@ -50,6 +54,7 @@ export const TableProvider = ({
const foundTable = tables.find(t => areTablesEqual(t.table, table));
if (foundTable) {
setColumns(foundTable.columns);
setComputedFields(foundTable.computedFields);
if (foundTable?.dataSource?.name !== rootTable?.dataSource?.name) return;
setRelationships(
foundTable.relationships.filter(rel => {
@ -82,6 +87,8 @@ export const TableProvider = ({
setRelationships,
objectPath,
loadRelationships,
computedFields,
setComputedFields,
]);
return (
@ -89,6 +96,8 @@ export const TableProvider = ({
value={{
columns,
setColumns,
computedFields,
setComputedFields,
table,
setTable: setTableName,
relationships,

View File

@ -19,6 +19,7 @@ export const tables: Tables = [
},
],
relationships: [],
computedFields: [],
},
{
table: ['Artist'],
@ -38,6 +39,7 @@ export const tables: Tables = [
},
],
relationships: [],
computedFields: [],
},
{
table: ['Album'],
@ -80,12 +82,14 @@ export const tables: Tables = [
},
},
],
computedFields: [],
},
{
table: ['Customer'],
dataSource: { name: 'SQLite', kind: 'SQLite' },
columns: [],
relationships: [],
computedFields: [],
},
{
table: { dataset: 'bigquery_sample', name: 'sample_table' },
@ -149,6 +153,7 @@ export const tables: Tables = [
},
],
relationships: [],
computedFields: [],
},
];
@ -189,6 +194,7 @@ export const tableWithGeolocationSupport = [
},
},
relationships: [],
computedFields: [],
columns: [
{
name: 'user_id',

View File

@ -2,6 +2,7 @@ import { Source, Table } from '../../../../../hasura-metadata-types';
import { GraphQLType } from 'graphql';
import { Relationship } from '../../../../../DatabaseRelationships';
import { TableColumn } from '../../../../../DataSource';
import { ComputedField } from '../../../../../../metadata/types';
export type Operators = Record<
string,
@ -22,6 +23,7 @@ export type Tables = Array<{
columns: Columns;
relationships: Relationships;
dataSource: Pick<Source, 'kind' | 'name'> | undefined;
computedFields: ComputedField[];
}>;
export type Operator = {
@ -40,6 +42,7 @@ export type Comparators = Record<string, Comparator>;
export type PermissionType =
| 'column'
| 'computedField'
| 'exist'
| 'relationship'
| 'object'
@ -77,6 +80,8 @@ export type TableContext = {
setComparator: (comparator: string | undefined) => void;
columns: Columns;
setColumns: (columns: Columns) => void;
computedFields: ComputedField[];
setComputedFields: (computedFields: ComputedField[]) => void;
relationships: Relationships;
setRelationships: (relationships: Relationships) => void;
};

View File

@ -10,6 +10,7 @@ import { rowPermissionsContext } from '../RowPermissionsProvider';
import { sourceDataTypes, SourceDataTypes } from './sourceDataTypes';
import { rootTableContext } from '../RootTableProvider';
import { columnDataType } from '../../../../../../DataSource/utils';
import { ComputedField } from '../../../../../../../metadata/types';
function columnOperators(): Array<Operator> {
return Object.keys(columnOperatorsInfo).reduce((acc, key) => {
@ -152,7 +153,7 @@ export const mapScalarDataType = (
export function useOperators({ path }: { path: string[] }) {
const { comparators } = useContext(rowPermissionsContext);
const { tables } = useContext(rootTableContext);
const { columns, table } = useContext(tableContext);
const { columns, table, computedFields } = useContext(tableContext);
const columnName = path[path.length - 2];
const column = columns.find(c => c.name === columnName);
@ -166,6 +167,7 @@ export function useOperators({ path }: { path: string[] }) {
comparators,
path,
columns,
computedFields,
tables,
table,
});
@ -181,6 +183,7 @@ export type GetDataTypeOperatorsProps = {
comparators: Comparators;
path: string[];
columns: Columns;
computedFields: ComputedField[];
tables: Tables;
table: Table;
};

View File

@ -92,6 +92,7 @@ const getInitialValue = (key: string, type?: PermissionType) => {
switch (type) {
case 'column':
case 'computedField':
// Depends on column type
return { _eq: '' };
case 'comparator':

View File

@ -39,6 +39,7 @@ export const usePermissionTables = ({
suggestedRelationships
),
columns,
computedFields: metadataTable.computed_fields ?? [],
};
}) ?? [],
};

View File

@ -12,6 +12,7 @@ import { SourceCustomization } from '../../../../../../hasura-metadata-types/sou
import { Operator } from '../../../../../../DataSource/types';
import {
ComputedField,
MetadataDataSource,
TableEntry,
} from '../../../../../../../metadata/types';
@ -41,6 +42,7 @@ export interface CreateDefaultValuesArgs {
dataSourceName: string;
metadata: Metadata | undefined;
tableColumns: TableColumn[];
tableComputedFields: ComputedField[];
defaultQueryRoot: string | never[];
metadataSource: MetadataDataSource | undefined;
supportedOperators: Operator[];
@ -52,6 +54,7 @@ export const createDefaultValues = ({
roleName,
table,
tableColumns,
tableComputedFields,
defaultQueryRoot,
metadataSource,
supportedOperators,
@ -74,6 +77,7 @@ export const createDefaultValues = ({
comment: '',
filterType: 'none',
columns: {},
computed_fields: {},
supportedOperators,
validateInput,
};
@ -84,6 +88,7 @@ export const createDefaultValues = ({
selectedTable,
roleName,
tableColumns,
tableComputedFields,
tableName,
metadataSource,
});

View File

@ -14,6 +14,7 @@ import { createDefaultValues } from '../../../../components/RowPermissionsBuilde
import type { QueryType } from '../../../../../types';
import {
ComputedField,
MetadataDataSource,
TableEntry,
} from '../../../../../../../metadata/types';
@ -83,6 +84,17 @@ const getColumns = (
}, {});
};
const getComputedFields = (
permissionComputedFields: string[],
tableComputedFields: ComputedField[]
) => {
return tableComputedFields.reduce<Record<string, boolean>>((acc, each) => {
const computedFieldIncluded = permissionComputedFields?.includes(each.name);
acc[each.name] = !!computedFieldIncluded;
return acc;
}, {});
};
export const createPermission = {
insert: (
permission: InsertPermissionDefinition,
@ -110,6 +122,7 @@ export const createPermission = {
select: (
permission: SelectPermissionDefinition,
tableColumns: TableColumn[],
tableComputedFields: ComputedField[],
tableName: string,
metadataSource: MetadataDataSource | undefined
) => {
@ -123,6 +136,10 @@ export const createPermission = {
const filterType = getCheckType(permission?.filter);
const columns = getColumns(permission?.columns || [], tableColumns);
const computed_fields = getComputedFields(
permission?.computed_fields || [],
tableComputedFields
);
const rowCount = getRowCount({
currentQueryPermissions: permission,
@ -135,6 +152,7 @@ export const createPermission = {
filter,
filterType,
columns,
computed_fields,
rowCount,
aggregationEnabled,
operators: ops,
@ -238,6 +256,7 @@ interface ObjArgs {
queryType: QueryType;
selectedTable: TableEntry;
tableColumns: TableColumn[];
tableComputedFields: ComputedField[];
roleName: string;
tableName: string;
metadataSource: MetadataDataSource | undefined;
@ -247,6 +266,7 @@ export const createPermissionsObject = ({
queryType,
selectedTable,
tableColumns,
tableComputedFields,
roleName,
tableName,
metadataSource,
@ -267,6 +287,7 @@ export const createPermissionsObject = ({
return createPermission.select(
selectedPermission.permission as SelectPermissionDefinition,
tableColumns,
tableComputedFields,
tableName,
// selectedTable.configuration,
metadataSource

View File

@ -2,6 +2,7 @@ import { TableColumn } from '../../../../../../DataSource';
import { Metadata } from '../../../../../../hasura-metadata-types';
import { isPermission } from '../../../../../utils';
import {
ComputedField,
MetadataDataSource,
TableEntry,
} from '../../../../../../../metadata/types';
@ -75,10 +76,12 @@ export interface CreateFormDataArgs {
metadataSource: MetadataDataSource;
trackedTables: TableEntry[];
validateInput: z.infer<typeof inputValidationSchema>;
computedFields: ComputedField[];
}
export const createFormData = (props: CreateFormDataArgs) => {
const { dataSourceName, table, tableColumns, trackedTables } = props;
const { dataSourceName, table, tableColumns, trackedTables, computedFields } =
props;
// find the specific metadata table
const metadataTable = getMetadataTable({
dataSourceName,
@ -93,5 +96,6 @@ export const createFormData = (props: CreateFormDataArgs) => {
supportedQueries,
tableNames: metadataTable.tableNames,
columns: tableColumns?.map(({ name }) => name),
computed_fields: computedFields.map(({ name }) => name),
};
};

View File

@ -75,6 +75,7 @@ export const useFormDataCreateDefaultValuesMock = {
role: 'asdf',
permission: {
columns: ['id', 'teacher'],
computed_fields: [],
filter: {
_exists: {
_table: { name: 'testing', schema: 'public' },
@ -165,6 +166,7 @@ export const useFormDataCreateDefaultValuesMock = {
role: 'new',
permission: {
columns: ['class', 'id'],
computed_fields: [],
filter: {},
allow_aggregations: true,
query_root_fields: [
@ -213,6 +215,7 @@ export const useFormDataCreateDefaultValuesMock = {
role: 'user',
permission: {
columns: ['deleted_at', 'id', 'metadata'],
computed_fields: [],
filter: { deleted_at: { _is_null: true } },
allow_aggregations: true,
},
@ -738,6 +741,7 @@ export const useFormDataCreateDefaultValuesMock = {
{ name: 'like', value: '_like', defaultValue: '%%' },
{ name: 'not like', value: '_nlike', defaultValue: '%%' },
],
tableComputedFields: [],
} as any;
export const createFormDataMock = {
@ -816,6 +820,7 @@ export const createFormDataMock = {
role: 'asdf',
permission: {
columns: ['id', 'teacher'],
computed_fields: [],
filter: {
_exists: {
_table: { name: 'testing', schema: 'public' },
@ -833,6 +838,7 @@ export const createFormDataMock = {
role: 'new',
permission: {
columns: ['id', 'teacher'],
computed_fields: [],
filter: {
_exists: {
_table: { name: 'testing', schema: 'public' },
@ -845,6 +851,7 @@ export const createFormDataMock = {
role: 'sdfsf',
permission: {
columns: ['id'],
computed_fields: [],
filter: {
_exists: {
_table: { name: 'class_student', schema: 'public' },
@ -859,6 +866,7 @@ export const createFormDataMock = {
role: 'testrole',
permission: {
columns: ['id', 'teacher'],
computed_fields: [],
filter: {
class_students: { class: { _eq: 'X-Hasura-User-Id' } },
},
@ -868,6 +876,7 @@ export const createFormDataMock = {
role: 'user',
permission: {
columns: ['id'],
computed_fields: [],
filter: {
_exists: {
_table: { name: 'class', schema: 'public' },
@ -906,6 +915,7 @@ export const createFormDataMock = {
role: 'new',
permission: {
columns: ['class', 'id'],
computed_fields: [],
filter: {},
allow_aggregations: true,
query_root_fields: [
@ -924,6 +934,7 @@ export const createFormDataMock = {
role: 'user',
permission: {
columns: ['class', 'id', 'student_id'],
computed_fields: [],
filter: {},
allow_aggregations: true,
},
@ -939,6 +950,7 @@ export const createFormDataMock = {
permission: {
check: {},
columns: ['id', 'metadata', 'deleted_at'],
computed_fields: [],
},
},
],
@ -947,6 +959,7 @@ export const createFormDataMock = {
role: 'asdf',
permission: {
columns: ['id', 'metadata', 'deleted_at'],
computed_fields: [],
filter: {},
},
},
@ -954,6 +967,7 @@ export const createFormDataMock = {
role: 'user',
permission: {
columns: ['deleted_at', 'id', 'metadata'],
computed_fields: [],
filter: { deleted_at: { _is_null: true } },
allow_aggregations: true,
},
@ -964,6 +978,7 @@ export const createFormDataMock = {
role: 'user',
permission: {
columns: ['id', 'metadata'],
computed_fields: [],
filter: {},
check: {},
},
@ -989,6 +1004,7 @@ export const createFormDataMock = {
role: 'sdfsf',
permission: {
columns: ['id', 'name', 'deleted_at'],
computed_fields: [],
filter: { _or: [] },
query_root_fields: ['select', 'select_by_pk'],
subscription_root_fields: ['select', 'select_by_pk'],
@ -998,6 +1014,7 @@ export const createFormDataMock = {
role: 'user',
permission: {
columns: ['deleted_at', 'id', 'name'],
computed_fields: [],
filter: { deleted_at: { _is_null: true } },
query_root_fields: ['select', 'select_by_pk'],
subscription_root_fields: ['select', 'select_by_pk'],
@ -1049,6 +1066,7 @@ export const createFormDataMock = {
role: 'asdf',
permission: {
columns: ['AlbumId'],
computed_fields: [],
filter: {
_or: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
},
@ -1058,6 +1076,7 @@ export const createFormDataMock = {
role: 'new',
permission: {
columns: ['AlbumId'],
computed_fields: [],
filter: {
_or: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
},
@ -1067,6 +1086,7 @@ export const createFormDataMock = {
role: 'sdfsf',
permission: {
columns: ['AlbumId', 'Title', 'ArtistId'],
computed_fields: [],
filter: {
_and: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
},
@ -1076,6 +1096,7 @@ export const createFormDataMock = {
role: 'testrole',
permission: {
columns: ['AlbumId', 'Title'],
computed_fields: [],
filter: { _and: [{ AlbumId: { _eq: 'X-Hasura-User' } }] },
},
},
@ -1094,6 +1115,7 @@ export const createFormDataMock = {
role: 'testrole',
permission: {
columns: [],
computed_fields: [],
filter: {
_exists: {
_table: ['Album'],
@ -1155,6 +1177,7 @@ export const createFormDataMock = {
role: 'asdf',
permission: {
columns: [],
computed_fields: [],
filter: { _not: { Data_value: { _eq: 1337 } } },
},
},
@ -1162,6 +1185,7 @@ export const createFormDataMock = {
role: 'new',
permission: {
columns: ['Series_reference', 'Period'],
computed_fields: [],
filter: {
_and: [
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
@ -1176,6 +1200,7 @@ export const createFormDataMock = {
{
role: 'sdfsf',
permission: {
computed_fields: [],
columns: [
'Series_reference',
'Period',
@ -1201,6 +1226,7 @@ export const createFormDataMock = {
role: 'testrole',
permission: {
columns: [],
computed_fields: [],
filter: { Magnitude: { _eq: '123' } },
},
},
@ -1223,6 +1249,7 @@ export const createFormDataMock = {
'Series_title_4',
'Series_title_5',
],
computed_fields: [],
filter: {},
},
},
@ -1382,6 +1409,7 @@ export const createFormDataMock = {
nullable: false,
},
],
computedFields: [],
trackedTables: [
{
table: { dataset: 'bigquery_sample', name: 'sample_table' },
@ -1425,6 +1453,7 @@ export const createFormDataMock = {
role: 'new',
permission: {
columns: ['Series_reference', 'Period'],
computed_fields: [],
filter: {
_and: [
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
@ -1439,6 +1468,7 @@ export const createFormDataMock = {
{
role: 'sdfsf',
permission: {
computed_fields: [],
columns: [
'Series_reference',
'Period',
@ -1462,11 +1492,16 @@ export const createFormDataMock = {
},
{
role: 'testrole',
permission: { columns: [], filter: { Magnitude: { _eq: '123' } } },
permission: {
columns: [],
computed_fields: [],
filter: { Magnitude: { _eq: '123' } },
},
},
{
role: 'user',
permission: {
computed_fields: [],
columns: [
'Series_reference',
'Period',
@ -1528,6 +1563,7 @@ export const createFormDataMock = {
role: 'asdf',
permission: {
columns: [],
computed_fields: [],
filter: { _not: { Data_value: { _eq: 1337 } } },
},
},
@ -1535,6 +1571,7 @@ export const createFormDataMock = {
role: 'new',
permission: {
columns: ['Series_reference', 'Period'],
computed_fields: [],
filter: {
_and: [
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
@ -1549,6 +1586,7 @@ export const createFormDataMock = {
{
role: 'sdfsf',
permission: {
computed_fields: [],
columns: [
'Series_reference',
'Period',
@ -1572,11 +1610,16 @@ export const createFormDataMock = {
},
{
role: 'testrole',
permission: { columns: [], filter: { Magnitude: { _eq: '123' } } },
permission: {
columns: [],
computed_fields: [],
filter: { Magnitude: { _eq: '123' } },
},
},
{
role: 'user',
permission: {
computed_fields: [],
columns: [
'Series_reference',
'Period',

View File

@ -22,6 +22,7 @@ const formDataMockResult = {
'Series_title_4',
'Series_title_5',
],
computed_fields: [],
};
test('returns correctly formatted formData', () => {
@ -79,6 +80,7 @@ const defaultValuesMockResult: ReturnType<typeof createDefaultValues> = {
},
query_root_fields: null,
subscription_root_fields: null,
computed_fields: {},
};
test('use default values returns values correctly', () => {

View File

@ -11,6 +11,8 @@ export const selectArgs: CreateInsertArgs = {
filterType: 'custom',
filter: { _not: { AlbumId: { _eq: 'X-Hasura-User-Id' } } },
columns: { AlbumId: true, Title: true, ArtistId: true },
computed_fields: {},
comment: '',
presets: [],
rowCount: '0',
aggregationEnabled: false,
@ -60,6 +62,7 @@ export const deleteArgs: CreateInsertArgs = {
{ name: '<=', value: '_lte' },
],
clonePermissions: [{ tableName: '', queryType: '', roleName: '' }],
comment: '',
},
accessType: 'partialAccess',
existingPermissions: [
@ -94,6 +97,8 @@ export const insertArgs: CreateInsertArgs = {
],
},
columns: { AlbumId: false, Title: false, ArtistId: false },
computed_fields: {},
comment: '',
presets: [{ columnName: 'default', presetType: 'static', columnValue: '' }],
backendOnly: false,
supportedOperators: [

View File

@ -2,6 +2,7 @@ import * as z from 'zod';
import { inputValidationSchema } from '../../../components/Services/Data/TablePermissions/InputValidation/InputValidation';
const columns = z.record(z.optional(z.boolean()));
const computed_fields = z.record(z.optional(z.boolean()));
const presets = z.optional(
z.array(
z.object({
@ -40,6 +41,7 @@ export const schema = z.discriminatedUnion('queryType', [
comment: z.string(),
check: z.any(),
columns,
computed_fields,
presets,
backendOnly: z.boolean().optional(),
supportedOperators: z.array(z.any()),
@ -52,6 +54,7 @@ export const schema = z.discriminatedUnion('queryType', [
comment: z.string(),
filter: z.any(),
columns,
computed_fields,
presets,
rowCount: z.string().optional(),
aggregationEnabled: z.boolean().optional(),
@ -64,6 +67,7 @@ export const schema = z.discriminatedUnion('queryType', [
z.object({
queryType: z.literal('update'),
columns,
computed_fields,
filterType: z.string(),
comment: z.string(),
filter: z.any(),

View File

@ -30,6 +30,7 @@ export interface SelectPermission extends BasePermission {
}
export interface SelectPermissionDefinition {
columns?: string[];
computed_fields?: string[];
filter?: Record<string, unknown>;
allow_aggregations?: boolean;
query_root_fields?: string[] | null;

View File

@ -1,3 +1,4 @@
import { QualifiedFunction } from '../../../metadata/types';
import {
InsertPermission,
SelectPermission,
@ -91,4 +92,11 @@ export type MetadataTable = {
apollo_federation_config?: {
enable: 'v1';
} | null;
computed_fields?: {
name: string;
definition: {
function: QualifiedFunction;
};
}[];
};