mirror of
https://github.com/hasura/graphql-engine.git
synced 2025-01-05 22:34:22 +03:00
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:
parent
73e413e1ee
commit
1b7f2451ca
@ -4,7 +4,7 @@ import { Button } from '../../../new-components/Button';
|
|||||||
import { IndicatorCard } from '../../../new-components/IndicatorCard';
|
import { IndicatorCard } from '../../../new-components/IndicatorCard';
|
||||||
import {
|
import {
|
||||||
MetadataSelector,
|
MetadataSelector,
|
||||||
useMetadata,
|
useMetadata as useLegacyMetadata,
|
||||||
useRoles,
|
useRoles,
|
||||||
useSupportedQueryTypes,
|
useSupportedQueryTypes,
|
||||||
} from '../../MetadataAPI';
|
} from '../../MetadataAPI';
|
||||||
@ -41,6 +41,7 @@ import {
|
|||||||
inputValidationEnabledSchema,
|
inputValidationEnabledSchema,
|
||||||
} from '../../../components/Services/Data/TablePermissions/InputValidation/InputValidation';
|
} from '../../../components/Services/Data/TablePermissions/InputValidation/InputValidation';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
import { MetadataSelectors, useMetadata } from '../../hasura-metadata-api';
|
||||||
|
|
||||||
export interface ComponentProps {
|
export interface ComponentProps {
|
||||||
dataSourceName: string;
|
dataSourceName: string;
|
||||||
@ -70,7 +71,7 @@ const Component = (props: ComponentProps) => {
|
|||||||
|
|
||||||
useScrollIntoView(permissionSectionRef, [roleName], { behavior: 'smooth' });
|
useScrollIntoView(permissionSectionRef, [roleName], { behavior: 'smooth' });
|
||||||
|
|
||||||
const { data: metadataTables } = useMetadata(
|
const { data: metadataTables } = useLegacyMetadata(
|
||||||
MetadataSelector.getTables(dataSourceName)
|
MetadataSelector.getTables(dataSourceName)
|
||||||
);
|
);
|
||||||
const tables = metadataTables?.map(t => t.table) ?? [];
|
const tables = metadataTables?.map(t => t.table) ?? [];
|
||||||
@ -197,6 +198,7 @@ const Component = (props: ComponentProps) => {
|
|||||||
roleName={roleName}
|
roleName={roleName}
|
||||||
queryType={queryType}
|
queryType={queryType}
|
||||||
columns={formData?.columns}
|
columns={formData?.columns}
|
||||||
|
computedFields={formData?.computed_fields}
|
||||||
table={table}
|
table={table}
|
||||||
dataSourceName={dataSourceName}
|
dataSourceName={dataSourceName}
|
||||||
/>
|
/>
|
||||||
@ -281,6 +283,11 @@ export const PermissionsForm = (props: PermissionsFormProps) => {
|
|||||||
const { columns: tableColumns, isLoading: isLoadingTables } =
|
const { columns: tableColumns, isLoading: isLoadingTables } =
|
||||||
useListAllTableColumns(dataSourceName, table);
|
useListAllTableColumns(dataSourceName, table);
|
||||||
|
|
||||||
|
const metadataTableResult = useMetadata(
|
||||||
|
MetadataSelectors.findTable(dataSourceName, table)
|
||||||
|
);
|
||||||
|
const computedFields = metadataTableResult.data?.computed_fields ?? [];
|
||||||
|
|
||||||
const { data: metadataSource } = useMetadataSource(dataSourceName);
|
const { data: metadataSource } = useMetadataSource(dataSourceName);
|
||||||
|
|
||||||
const { data, isError, isLoading } = useFormData({
|
const { data, isError, isLoading } = useFormData({
|
||||||
@ -328,6 +335,7 @@ export const PermissionsForm = (props: PermissionsFormProps) => {
|
|||||||
metadata: data?.metadata,
|
metadata: data?.metadata,
|
||||||
table,
|
table,
|
||||||
tableColumns,
|
tableColumns,
|
||||||
|
tableComputedFields: computedFields,
|
||||||
defaultQueryRoot: data.defaultQueryRoot,
|
defaultQueryRoot: data.defaultQueryRoot,
|
||||||
metadataSource,
|
metadataSource,
|
||||||
supportedOperators: data.supportedOperators,
|
supportedOperators: data.supportedOperators,
|
||||||
@ -357,6 +365,7 @@ export const PermissionsForm = (props: PermissionsFormProps) => {
|
|||||||
table,
|
table,
|
||||||
metadata: data.metadata,
|
metadata: data.metadata,
|
||||||
tableColumns,
|
tableColumns,
|
||||||
|
computedFields,
|
||||||
trackedTables: metadataSource.tables,
|
trackedTables: metadataSource.tables,
|
||||||
metadataSource,
|
metadataSource,
|
||||||
validateInput: {
|
validateInput: {
|
||||||
|
@ -17,11 +17,13 @@ test('create select args object from form data', () => {
|
|||||||
args: {
|
args: {
|
||||||
table: ['Album'],
|
table: ['Album'],
|
||||||
role: 'user',
|
role: 'user',
|
||||||
|
comment: '',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['AlbumId', 'Title', 'ArtistId'],
|
columns: ['AlbumId', 'Title', 'ArtistId'],
|
||||||
filter: { _not: { AlbumId: { _eq: 'X-Hasura-User-Id' } } },
|
filter: { _not: { AlbumId: { _eq: 'X-Hasura-User-Id' } } },
|
||||||
set: {},
|
set: {},
|
||||||
allow_aggregations: false,
|
allow_aggregations: false,
|
||||||
|
computed_fields: [],
|
||||||
},
|
},
|
||||||
source: 'Chinook',
|
source: 'Chinook',
|
||||||
},
|
},
|
||||||
@ -42,6 +44,7 @@ test('create delete args object from form data', () => {
|
|||||||
args: {
|
args: {
|
||||||
table: ['Album'],
|
table: ['Album'],
|
||||||
role: 'user',
|
role: 'user',
|
||||||
|
comment: '',
|
||||||
permission: { backend_only: false, filter: { Title: { _eq: 'Test' } } },
|
permission: { backend_only: false, filter: { Title: { _eq: 'Test' } } },
|
||||||
source: 'Chinook',
|
source: 'Chinook',
|
||||||
},
|
},
|
||||||
@ -58,6 +61,7 @@ test('create insert args object from form data', () => {
|
|||||||
args: {
|
args: {
|
||||||
table: ['Album'],
|
table: ['Album'],
|
||||||
role: 'user',
|
role: 'user',
|
||||||
|
comment: '',
|
||||||
permission: {
|
permission: {
|
||||||
columns: [],
|
columns: [],
|
||||||
check: {
|
check: {
|
||||||
@ -69,6 +73,7 @@ test('create insert args object from form data', () => {
|
|||||||
},
|
},
|
||||||
allow_upsert: true,
|
allow_upsert: true,
|
||||||
set: {},
|
set: {},
|
||||||
|
validate_input: undefined,
|
||||||
backend_only: false,
|
backend_only: false,
|
||||||
},
|
},
|
||||||
source: 'Chinook',
|
source: 'Chinook',
|
||||||
|
@ -30,6 +30,7 @@ const formatFilterValues = (formFilter: Record<string, any>[] = []) => {
|
|||||||
|
|
||||||
type SelectPermissionMetadata = {
|
type SelectPermissionMetadata = {
|
||||||
columns: string[];
|
columns: string[];
|
||||||
|
computed_fields: string[];
|
||||||
set: Record<string, any>;
|
set: Record<string, any>;
|
||||||
filter: Record<string, any>;
|
filter: Record<string, any>;
|
||||||
allow_aggregations?: boolean;
|
allow_aggregations?: boolean;
|
||||||
@ -43,12 +44,16 @@ const createSelectObject = (input: PermissionsSchema) => {
|
|||||||
const columns = Object.entries(input.columns)
|
const columns = Object.entries(input.columns)
|
||||||
.filter(({ 1: value }) => value)
|
.filter(({ 1: value }) => value)
|
||||||
.map(([key]) => key);
|
.map(([key]) => key);
|
||||||
|
const computed_fields = Object.entries(input.computed_fields)
|
||||||
|
.filter(({ 1: value }) => value)
|
||||||
|
.map(([key]) => key);
|
||||||
|
|
||||||
// Input may be undefined
|
// Input may be undefined
|
||||||
const filter = formatFilterValues(input.filter);
|
const filter = formatFilterValues(input.filter);
|
||||||
|
|
||||||
const permissionObject: SelectPermissionMetadata = {
|
const permissionObject: SelectPermissionMetadata = {
|
||||||
columns,
|
columns,
|
||||||
|
computed_fields,
|
||||||
filter,
|
filter,
|
||||||
set: {},
|
set: {},
|
||||||
allow_aggregations: input.aggregationEnabled,
|
allow_aggregations: input.aggregationEnabled,
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
SubscriptionRootPermissionType,
|
SubscriptionRootPermissionType,
|
||||||
QueryRootPermissionType,
|
QueryRootPermissionType,
|
||||||
} from './RootFieldPermissions/types';
|
} from './RootFieldPermissions/types';
|
||||||
|
import { MetadataSelectors, useMetadata } from '../../../hasura-metadata-api';
|
||||||
|
|
||||||
const getAccessText = (queryType: string) => {
|
const getAccessText = (queryType: string) => {
|
||||||
if (queryType === 'insert') {
|
if (queryType === 'insert') {
|
||||||
@ -35,6 +36,7 @@ export interface ColumnPermissionsSectionProps {
|
|||||||
queryType: QueryType;
|
queryType: QueryType;
|
||||||
roleName: string;
|
roleName: string;
|
||||||
columns?: string[];
|
columns?: string[];
|
||||||
|
computedFields?: string[];
|
||||||
table: unknown;
|
table: unknown;
|
||||||
dataSourceName: string;
|
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<
|
export const ColumnPermissionsSection: React.FC<
|
||||||
ColumnPermissionsSectionProps
|
ColumnPermissionsSectionProps
|
||||||
> = ({ roleName, queryType, columns, table, dataSourceName }) => {
|
> = ({
|
||||||
|
roleName,
|
||||||
|
queryType,
|
||||||
|
columns,
|
||||||
|
table,
|
||||||
|
computedFields,
|
||||||
|
dataSourceName,
|
||||||
|
}) => {
|
||||||
const { setValue, watch } = useFormContext();
|
const { setValue, watch } = useFormContext();
|
||||||
const [showConfirmation, setShowConfirmationModal] = useState<string | null>(
|
const [showConfirmation, setShowConfirmationModal] = useState<string | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
watch();
|
watch();
|
||||||
|
|
||||||
const [selectedColumns, queryRootFields, subscriptionRootFields] = watch([
|
const [
|
||||||
|
selectedColumns,
|
||||||
|
selectedComputedFields,
|
||||||
|
queryRootFields,
|
||||||
|
subscriptionRootFields,
|
||||||
|
] = watch([
|
||||||
'columns',
|
'columns',
|
||||||
|
'computed_fields',
|
||||||
'query_root_fields',
|
'query_root_fields',
|
||||||
'subscription_root_fields',
|
'subscription_root_fields',
|
||||||
]);
|
]);
|
||||||
@ -112,6 +125,13 @@ export const ColumnPermissionsSection: React.FC<
|
|||||||
table
|
table
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const metadataTableResult = useMetadata(
|
||||||
|
MetadataSelectors.findTable(dataSourceName, table)
|
||||||
|
);
|
||||||
|
const tableComputedFields = metadataTableResult.data?.computed_fields?.map(
|
||||||
|
({ name }) => name
|
||||||
|
);
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
columns?.forEach(column => {
|
columns?.forEach(column => {
|
||||||
const toggleAllOn = status !== 'All columns';
|
const toggleAllOn = status !== 'All columns';
|
||||||
@ -119,6 +139,12 @@ export const ColumnPermissionsSection: React.FC<
|
|||||||
// otherwise toggle all off
|
// otherwise toggle all off
|
||||||
setValue(`columns.${column}`, toggleAllOn);
|
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) {
|
if (isError) {
|
||||||
@ -206,6 +232,26 @@ export const ColumnPermissionsSection: React.FC<
|
|||||||
<i>{fieldName}</i>
|
<i>{fieldName}</i>
|
||||||
</label>
|
</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
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
@ -21,7 +21,8 @@ export const Operator = ({
|
|||||||
rowPermissionsContext
|
rowPermissionsContext
|
||||||
);
|
);
|
||||||
const { tables } = useContext(rootTableContext);
|
const { tables } = useContext(rootTableContext);
|
||||||
const { columns, table, relationships } = useContext(tableContext);
|
const { columns, table, relationships, computedFields } =
|
||||||
|
useContext(tableContext);
|
||||||
const { rootLogicalModel } = useContext(logicalModelContext);
|
const { rootLogicalModel } = useContext(logicalModelContext);
|
||||||
const parent = path[path.length - 1];
|
const parent = path[path.length - 1];
|
||||||
const operatorLevelId =
|
const operatorLevelId =
|
||||||
@ -73,6 +74,19 @@ export const Operator = ({
|
|||||||
))}
|
))}
|
||||||
</optgroup>
|
</optgroup>
|
||||||
) : null}
|
) : 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 ? (
|
{rootLogicalModel?.fields.length ? (
|
||||||
<optgroup label="Columns">
|
<optgroup label="Columns">
|
||||||
{rootLogicalModel?.fields.map((field, index) => (
|
{rootLogicalModel?.fields.map((field, index) => (
|
||||||
|
@ -5,6 +5,7 @@ import { rootTableContext } from './RootTableProvider';
|
|||||||
import { areTablesEqual } from '../../../../../hasura-metadata-api';
|
import { areTablesEqual } from '../../../../../hasura-metadata-api';
|
||||||
import { fieldsToColumns } from './utils/nestedObjects';
|
import { fieldsToColumns } from './utils/nestedObjects';
|
||||||
import { rowPermissionsContext } from './RowPermissionsProvider';
|
import { rowPermissionsContext } from './RowPermissionsProvider';
|
||||||
|
import { ComputedField } from '../../../../../../metadata/types';
|
||||||
|
|
||||||
export const tableContext = createContext<TableContext>({
|
export const tableContext = createContext<TableContext>({
|
||||||
table: {},
|
table: {},
|
||||||
@ -13,6 +14,8 @@ export const tableContext = createContext<TableContext>({
|
|||||||
setComparator: () => {},
|
setComparator: () => {},
|
||||||
columns: [],
|
columns: [],
|
||||||
setColumns: () => {},
|
setColumns: () => {},
|
||||||
|
computedFields: [],
|
||||||
|
setComputedFields: () => {},
|
||||||
relationships: [],
|
relationships: [],
|
||||||
setRelationships: () => {},
|
setRelationships: () => {},
|
||||||
});
|
});
|
||||||
@ -29,6 +32,7 @@ export const TableProvider = ({
|
|||||||
const [table, setTableName] = useState<Table>(defaultTable || {});
|
const [table, setTableName] = useState<Table>(defaultTable || {});
|
||||||
const [comparator, setComparator] = useState<string | undefined>();
|
const [comparator, setComparator] = useState<string | undefined>();
|
||||||
const [columns, setColumns] = useState<Columns>([]);
|
const [columns, setColumns] = useState<Columns>([]);
|
||||||
|
const [computedFields, setComputedFields] = useState<ComputedField[]>([]);
|
||||||
const [relationships, setRelationships] = useState<Relationships>([]);
|
const [relationships, setRelationships] = useState<Relationships>([]);
|
||||||
const { tables, rootTable } = useContext(rootTableContext);
|
const { tables, rootTable } = useContext(rootTableContext);
|
||||||
const { loadRelationships } = useContext(rowPermissionsContext);
|
const { loadRelationships } = useContext(rowPermissionsContext);
|
||||||
@ -50,6 +54,7 @@ export const TableProvider = ({
|
|||||||
const foundTable = tables.find(t => areTablesEqual(t.table, table));
|
const foundTable = tables.find(t => areTablesEqual(t.table, table));
|
||||||
if (foundTable) {
|
if (foundTable) {
|
||||||
setColumns(foundTable.columns);
|
setColumns(foundTable.columns);
|
||||||
|
setComputedFields(foundTable.computedFields);
|
||||||
if (foundTable?.dataSource?.name !== rootTable?.dataSource?.name) return;
|
if (foundTable?.dataSource?.name !== rootTable?.dataSource?.name) return;
|
||||||
setRelationships(
|
setRelationships(
|
||||||
foundTable.relationships.filter(rel => {
|
foundTable.relationships.filter(rel => {
|
||||||
@ -82,6 +87,8 @@ export const TableProvider = ({
|
|||||||
setRelationships,
|
setRelationships,
|
||||||
objectPath,
|
objectPath,
|
||||||
loadRelationships,
|
loadRelationships,
|
||||||
|
computedFields,
|
||||||
|
setComputedFields,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -89,6 +96,8 @@ export const TableProvider = ({
|
|||||||
value={{
|
value={{
|
||||||
columns,
|
columns,
|
||||||
setColumns,
|
setColumns,
|
||||||
|
computedFields,
|
||||||
|
setComputedFields,
|
||||||
table,
|
table,
|
||||||
setTable: setTableName,
|
setTable: setTableName,
|
||||||
relationships,
|
relationships,
|
||||||
|
@ -19,6 +19,7 @@ export const tables: Tables = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
|
computedFields: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
table: ['Artist'],
|
table: ['Artist'],
|
||||||
@ -38,6 +39,7 @@ export const tables: Tables = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
|
computedFields: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
table: ['Album'],
|
table: ['Album'],
|
||||||
@ -80,12 +82,14 @@ export const tables: Tables = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
computedFields: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
table: ['Customer'],
|
table: ['Customer'],
|
||||||
dataSource: { name: 'SQLite', kind: 'SQLite' },
|
dataSource: { name: 'SQLite', kind: 'SQLite' },
|
||||||
columns: [],
|
columns: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
|
computedFields: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
table: { dataset: 'bigquery_sample', name: 'sample_table' },
|
table: { dataset: 'bigquery_sample', name: 'sample_table' },
|
||||||
@ -149,6 +153,7 @@ export const tables: Tables = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
|
computedFields: [],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -189,6 +194,7 @@ export const tableWithGeolocationSupport = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
relationships: [],
|
relationships: [],
|
||||||
|
computedFields: [],
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
name: 'user_id',
|
name: 'user_id',
|
||||||
|
@ -2,6 +2,7 @@ import { Source, Table } from '../../../../../hasura-metadata-types';
|
|||||||
import { GraphQLType } from 'graphql';
|
import { GraphQLType } from 'graphql';
|
||||||
import { Relationship } from '../../../../../DatabaseRelationships';
|
import { Relationship } from '../../../../../DatabaseRelationships';
|
||||||
import { TableColumn } from '../../../../../DataSource';
|
import { TableColumn } from '../../../../../DataSource';
|
||||||
|
import { ComputedField } from '../../../../../../metadata/types';
|
||||||
|
|
||||||
export type Operators = Record<
|
export type Operators = Record<
|
||||||
string,
|
string,
|
||||||
@ -22,6 +23,7 @@ export type Tables = Array<{
|
|||||||
columns: Columns;
|
columns: Columns;
|
||||||
relationships: Relationships;
|
relationships: Relationships;
|
||||||
dataSource: Pick<Source, 'kind' | 'name'> | undefined;
|
dataSource: Pick<Source, 'kind' | 'name'> | undefined;
|
||||||
|
computedFields: ComputedField[];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type Operator = {
|
export type Operator = {
|
||||||
@ -40,6 +42,7 @@ export type Comparators = Record<string, Comparator>;
|
|||||||
|
|
||||||
export type PermissionType =
|
export type PermissionType =
|
||||||
| 'column'
|
| 'column'
|
||||||
|
| 'computedField'
|
||||||
| 'exist'
|
| 'exist'
|
||||||
| 'relationship'
|
| 'relationship'
|
||||||
| 'object'
|
| 'object'
|
||||||
@ -77,6 +80,8 @@ export type TableContext = {
|
|||||||
setComparator: (comparator: string | undefined) => void;
|
setComparator: (comparator: string | undefined) => void;
|
||||||
columns: Columns;
|
columns: Columns;
|
||||||
setColumns: (columns: Columns) => void;
|
setColumns: (columns: Columns) => void;
|
||||||
|
computedFields: ComputedField[];
|
||||||
|
setComputedFields: (computedFields: ComputedField[]) => void;
|
||||||
relationships: Relationships;
|
relationships: Relationships;
|
||||||
setRelationships: (relationships: Relationships) => void;
|
setRelationships: (relationships: Relationships) => void;
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@ import { rowPermissionsContext } from '../RowPermissionsProvider';
|
|||||||
import { sourceDataTypes, SourceDataTypes } from './sourceDataTypes';
|
import { sourceDataTypes, SourceDataTypes } from './sourceDataTypes';
|
||||||
import { rootTableContext } from '../RootTableProvider';
|
import { rootTableContext } from '../RootTableProvider';
|
||||||
import { columnDataType } from '../../../../../../DataSource/utils';
|
import { columnDataType } from '../../../../../../DataSource/utils';
|
||||||
|
import { ComputedField } from '../../../../../../../metadata/types';
|
||||||
|
|
||||||
function columnOperators(): Array<Operator> {
|
function columnOperators(): Array<Operator> {
|
||||||
return Object.keys(columnOperatorsInfo).reduce((acc, key) => {
|
return Object.keys(columnOperatorsInfo).reduce((acc, key) => {
|
||||||
@ -152,7 +153,7 @@ export const mapScalarDataType = (
|
|||||||
export function useOperators({ path }: { path: string[] }) {
|
export function useOperators({ path }: { path: string[] }) {
|
||||||
const { comparators } = useContext(rowPermissionsContext);
|
const { comparators } = useContext(rowPermissionsContext);
|
||||||
const { tables } = useContext(rootTableContext);
|
const { tables } = useContext(rootTableContext);
|
||||||
const { columns, table } = useContext(tableContext);
|
const { columns, table, computedFields } = useContext(tableContext);
|
||||||
|
|
||||||
const columnName = path[path.length - 2];
|
const columnName = path[path.length - 2];
|
||||||
const column = columns.find(c => c.name === columnName);
|
const column = columns.find(c => c.name === columnName);
|
||||||
@ -166,6 +167,7 @@ export function useOperators({ path }: { path: string[] }) {
|
|||||||
comparators,
|
comparators,
|
||||||
path,
|
path,
|
||||||
columns,
|
columns,
|
||||||
|
computedFields,
|
||||||
tables,
|
tables,
|
||||||
table,
|
table,
|
||||||
});
|
});
|
||||||
@ -181,6 +183,7 @@ export type GetDataTypeOperatorsProps = {
|
|||||||
comparators: Comparators;
|
comparators: Comparators;
|
||||||
path: string[];
|
path: string[];
|
||||||
columns: Columns;
|
columns: Columns;
|
||||||
|
computedFields: ComputedField[];
|
||||||
tables: Tables;
|
tables: Tables;
|
||||||
table: Table;
|
table: Table;
|
||||||
};
|
};
|
||||||
|
@ -92,6 +92,7 @@ const getInitialValue = (key: string, type?: PermissionType) => {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'column':
|
case 'column':
|
||||||
|
case 'computedField':
|
||||||
// Depends on column type
|
// Depends on column type
|
||||||
return { _eq: '' };
|
return { _eq: '' };
|
||||||
case 'comparator':
|
case 'comparator':
|
||||||
|
@ -39,6 +39,7 @@ export const usePermissionTables = ({
|
|||||||
suggestedRelationships
|
suggestedRelationships
|
||||||
),
|
),
|
||||||
columns,
|
columns,
|
||||||
|
computedFields: metadataTable.computed_fields ?? [],
|
||||||
};
|
};
|
||||||
}) ?? [],
|
}) ?? [],
|
||||||
};
|
};
|
||||||
|
@ -12,6 +12,7 @@ import { SourceCustomization } from '../../../../../../hasura-metadata-types/sou
|
|||||||
import { Operator } from '../../../../../../DataSource/types';
|
import { Operator } from '../../../../../../DataSource/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ComputedField,
|
||||||
MetadataDataSource,
|
MetadataDataSource,
|
||||||
TableEntry,
|
TableEntry,
|
||||||
} from '../../../../../../../metadata/types';
|
} from '../../../../../../../metadata/types';
|
||||||
@ -41,6 +42,7 @@ export interface CreateDefaultValuesArgs {
|
|||||||
dataSourceName: string;
|
dataSourceName: string;
|
||||||
metadata: Metadata | undefined;
|
metadata: Metadata | undefined;
|
||||||
tableColumns: TableColumn[];
|
tableColumns: TableColumn[];
|
||||||
|
tableComputedFields: ComputedField[];
|
||||||
defaultQueryRoot: string | never[];
|
defaultQueryRoot: string | never[];
|
||||||
metadataSource: MetadataDataSource | undefined;
|
metadataSource: MetadataDataSource | undefined;
|
||||||
supportedOperators: Operator[];
|
supportedOperators: Operator[];
|
||||||
@ -52,6 +54,7 @@ export const createDefaultValues = ({
|
|||||||
roleName,
|
roleName,
|
||||||
table,
|
table,
|
||||||
tableColumns,
|
tableColumns,
|
||||||
|
tableComputedFields,
|
||||||
defaultQueryRoot,
|
defaultQueryRoot,
|
||||||
metadataSource,
|
metadataSource,
|
||||||
supportedOperators,
|
supportedOperators,
|
||||||
@ -74,6 +77,7 @@ export const createDefaultValues = ({
|
|||||||
comment: '',
|
comment: '',
|
||||||
filterType: 'none',
|
filterType: 'none',
|
||||||
columns: {},
|
columns: {},
|
||||||
|
computed_fields: {},
|
||||||
supportedOperators,
|
supportedOperators,
|
||||||
validateInput,
|
validateInput,
|
||||||
};
|
};
|
||||||
@ -84,6 +88,7 @@ export const createDefaultValues = ({
|
|||||||
selectedTable,
|
selectedTable,
|
||||||
roleName,
|
roleName,
|
||||||
tableColumns,
|
tableColumns,
|
||||||
|
tableComputedFields,
|
||||||
tableName,
|
tableName,
|
||||||
metadataSource,
|
metadataSource,
|
||||||
});
|
});
|
||||||
|
@ -14,6 +14,7 @@ import { createDefaultValues } from '../../../../components/RowPermissionsBuilde
|
|||||||
|
|
||||||
import type { QueryType } from '../../../../../types';
|
import type { QueryType } from '../../../../../types';
|
||||||
import {
|
import {
|
||||||
|
ComputedField,
|
||||||
MetadataDataSource,
|
MetadataDataSource,
|
||||||
TableEntry,
|
TableEntry,
|
||||||
} from '../../../../../../../metadata/types';
|
} 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 = {
|
export const createPermission = {
|
||||||
insert: (
|
insert: (
|
||||||
permission: InsertPermissionDefinition,
|
permission: InsertPermissionDefinition,
|
||||||
@ -110,6 +122,7 @@ export const createPermission = {
|
|||||||
select: (
|
select: (
|
||||||
permission: SelectPermissionDefinition,
|
permission: SelectPermissionDefinition,
|
||||||
tableColumns: TableColumn[],
|
tableColumns: TableColumn[],
|
||||||
|
tableComputedFields: ComputedField[],
|
||||||
tableName: string,
|
tableName: string,
|
||||||
metadataSource: MetadataDataSource | undefined
|
metadataSource: MetadataDataSource | undefined
|
||||||
) => {
|
) => {
|
||||||
@ -123,6 +136,10 @@ export const createPermission = {
|
|||||||
const filterType = getCheckType(permission?.filter);
|
const filterType = getCheckType(permission?.filter);
|
||||||
|
|
||||||
const columns = getColumns(permission?.columns || [], tableColumns);
|
const columns = getColumns(permission?.columns || [], tableColumns);
|
||||||
|
const computed_fields = getComputedFields(
|
||||||
|
permission?.computed_fields || [],
|
||||||
|
tableComputedFields
|
||||||
|
);
|
||||||
|
|
||||||
const rowCount = getRowCount({
|
const rowCount = getRowCount({
|
||||||
currentQueryPermissions: permission,
|
currentQueryPermissions: permission,
|
||||||
@ -135,6 +152,7 @@ export const createPermission = {
|
|||||||
filter,
|
filter,
|
||||||
filterType,
|
filterType,
|
||||||
columns,
|
columns,
|
||||||
|
computed_fields,
|
||||||
rowCount,
|
rowCount,
|
||||||
aggregationEnabled,
|
aggregationEnabled,
|
||||||
operators: ops,
|
operators: ops,
|
||||||
@ -238,6 +256,7 @@ interface ObjArgs {
|
|||||||
queryType: QueryType;
|
queryType: QueryType;
|
||||||
selectedTable: TableEntry;
|
selectedTable: TableEntry;
|
||||||
tableColumns: TableColumn[];
|
tableColumns: TableColumn[];
|
||||||
|
tableComputedFields: ComputedField[];
|
||||||
roleName: string;
|
roleName: string;
|
||||||
tableName: string;
|
tableName: string;
|
||||||
metadataSource: MetadataDataSource | undefined;
|
metadataSource: MetadataDataSource | undefined;
|
||||||
@ -247,6 +266,7 @@ export const createPermissionsObject = ({
|
|||||||
queryType,
|
queryType,
|
||||||
selectedTable,
|
selectedTable,
|
||||||
tableColumns,
|
tableColumns,
|
||||||
|
tableComputedFields,
|
||||||
roleName,
|
roleName,
|
||||||
tableName,
|
tableName,
|
||||||
metadataSource,
|
metadataSource,
|
||||||
@ -267,6 +287,7 @@ export const createPermissionsObject = ({
|
|||||||
return createPermission.select(
|
return createPermission.select(
|
||||||
selectedPermission.permission as SelectPermissionDefinition,
|
selectedPermission.permission as SelectPermissionDefinition,
|
||||||
tableColumns,
|
tableColumns,
|
||||||
|
tableComputedFields,
|
||||||
tableName,
|
tableName,
|
||||||
// selectedTable.configuration,
|
// selectedTable.configuration,
|
||||||
metadataSource
|
metadataSource
|
||||||
|
@ -2,6 +2,7 @@ import { TableColumn } from '../../../../../../DataSource';
|
|||||||
import { Metadata } from '../../../../../../hasura-metadata-types';
|
import { Metadata } from '../../../../../../hasura-metadata-types';
|
||||||
import { isPermission } from '../../../../../utils';
|
import { isPermission } from '../../../../../utils';
|
||||||
import {
|
import {
|
||||||
|
ComputedField,
|
||||||
MetadataDataSource,
|
MetadataDataSource,
|
||||||
TableEntry,
|
TableEntry,
|
||||||
} from '../../../../../../../metadata/types';
|
} from '../../../../../../../metadata/types';
|
||||||
@ -75,10 +76,12 @@ export interface CreateFormDataArgs {
|
|||||||
metadataSource: MetadataDataSource;
|
metadataSource: MetadataDataSource;
|
||||||
trackedTables: TableEntry[];
|
trackedTables: TableEntry[];
|
||||||
validateInput: z.infer<typeof inputValidationSchema>;
|
validateInput: z.infer<typeof inputValidationSchema>;
|
||||||
|
computedFields: ComputedField[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createFormData = (props: CreateFormDataArgs) => {
|
export const createFormData = (props: CreateFormDataArgs) => {
|
||||||
const { dataSourceName, table, tableColumns, trackedTables } = props;
|
const { dataSourceName, table, tableColumns, trackedTables, computedFields } =
|
||||||
|
props;
|
||||||
// find the specific metadata table
|
// find the specific metadata table
|
||||||
const metadataTable = getMetadataTable({
|
const metadataTable = getMetadataTable({
|
||||||
dataSourceName,
|
dataSourceName,
|
||||||
@ -93,5 +96,6 @@ export const createFormData = (props: CreateFormDataArgs) => {
|
|||||||
supportedQueries,
|
supportedQueries,
|
||||||
tableNames: metadataTable.tableNames,
|
tableNames: metadataTable.tableNames,
|
||||||
columns: tableColumns?.map(({ name }) => name),
|
columns: tableColumns?.map(({ name }) => name),
|
||||||
|
computed_fields: computedFields.map(({ name }) => name),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -75,6 +75,7 @@ export const useFormDataCreateDefaultValuesMock = {
|
|||||||
role: 'asdf',
|
role: 'asdf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id', 'teacher'],
|
columns: ['id', 'teacher'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_exists: {
|
_exists: {
|
||||||
_table: { name: 'testing', schema: 'public' },
|
_table: { name: 'testing', schema: 'public' },
|
||||||
@ -165,6 +166,7 @@ export const useFormDataCreateDefaultValuesMock = {
|
|||||||
role: 'new',
|
role: 'new',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['class', 'id'],
|
columns: ['class', 'id'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {},
|
filter: {},
|
||||||
allow_aggregations: true,
|
allow_aggregations: true,
|
||||||
query_root_fields: [
|
query_root_fields: [
|
||||||
@ -213,6 +215,7 @@ export const useFormDataCreateDefaultValuesMock = {
|
|||||||
role: 'user',
|
role: 'user',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['deleted_at', 'id', 'metadata'],
|
columns: ['deleted_at', 'id', 'metadata'],
|
||||||
|
computed_fields: [],
|
||||||
filter: { deleted_at: { _is_null: true } },
|
filter: { deleted_at: { _is_null: true } },
|
||||||
allow_aggregations: true,
|
allow_aggregations: true,
|
||||||
},
|
},
|
||||||
@ -738,6 +741,7 @@ export const useFormDataCreateDefaultValuesMock = {
|
|||||||
{ name: 'like', value: '_like', defaultValue: '%%' },
|
{ name: 'like', value: '_like', defaultValue: '%%' },
|
||||||
{ name: 'not like', value: '_nlike', defaultValue: '%%' },
|
{ name: 'not like', value: '_nlike', defaultValue: '%%' },
|
||||||
],
|
],
|
||||||
|
tableComputedFields: [],
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
export const createFormDataMock = {
|
export const createFormDataMock = {
|
||||||
@ -816,6 +820,7 @@ export const createFormDataMock = {
|
|||||||
role: 'asdf',
|
role: 'asdf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id', 'teacher'],
|
columns: ['id', 'teacher'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_exists: {
|
_exists: {
|
||||||
_table: { name: 'testing', schema: 'public' },
|
_table: { name: 'testing', schema: 'public' },
|
||||||
@ -833,6 +838,7 @@ export const createFormDataMock = {
|
|||||||
role: 'new',
|
role: 'new',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id', 'teacher'],
|
columns: ['id', 'teacher'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_exists: {
|
_exists: {
|
||||||
_table: { name: 'testing', schema: 'public' },
|
_table: { name: 'testing', schema: 'public' },
|
||||||
@ -845,6 +851,7 @@ export const createFormDataMock = {
|
|||||||
role: 'sdfsf',
|
role: 'sdfsf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id'],
|
columns: ['id'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_exists: {
|
_exists: {
|
||||||
_table: { name: 'class_student', schema: 'public' },
|
_table: { name: 'class_student', schema: 'public' },
|
||||||
@ -859,6 +866,7 @@ export const createFormDataMock = {
|
|||||||
role: 'testrole',
|
role: 'testrole',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id', 'teacher'],
|
columns: ['id', 'teacher'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
class_students: { class: { _eq: 'X-Hasura-User-Id' } },
|
class_students: { class: { _eq: 'X-Hasura-User-Id' } },
|
||||||
},
|
},
|
||||||
@ -868,6 +876,7 @@ export const createFormDataMock = {
|
|||||||
role: 'user',
|
role: 'user',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id'],
|
columns: ['id'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_exists: {
|
_exists: {
|
||||||
_table: { name: 'class', schema: 'public' },
|
_table: { name: 'class', schema: 'public' },
|
||||||
@ -906,6 +915,7 @@ export const createFormDataMock = {
|
|||||||
role: 'new',
|
role: 'new',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['class', 'id'],
|
columns: ['class', 'id'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {},
|
filter: {},
|
||||||
allow_aggregations: true,
|
allow_aggregations: true,
|
||||||
query_root_fields: [
|
query_root_fields: [
|
||||||
@ -924,6 +934,7 @@ export const createFormDataMock = {
|
|||||||
role: 'user',
|
role: 'user',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['class', 'id', 'student_id'],
|
columns: ['class', 'id', 'student_id'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {},
|
filter: {},
|
||||||
allow_aggregations: true,
|
allow_aggregations: true,
|
||||||
},
|
},
|
||||||
@ -939,6 +950,7 @@ export const createFormDataMock = {
|
|||||||
permission: {
|
permission: {
|
||||||
check: {},
|
check: {},
|
||||||
columns: ['id', 'metadata', 'deleted_at'],
|
columns: ['id', 'metadata', 'deleted_at'],
|
||||||
|
computed_fields: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -947,6 +959,7 @@ export const createFormDataMock = {
|
|||||||
role: 'asdf',
|
role: 'asdf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id', 'metadata', 'deleted_at'],
|
columns: ['id', 'metadata', 'deleted_at'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {},
|
filter: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -954,6 +967,7 @@ export const createFormDataMock = {
|
|||||||
role: 'user',
|
role: 'user',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['deleted_at', 'id', 'metadata'],
|
columns: ['deleted_at', 'id', 'metadata'],
|
||||||
|
computed_fields: [],
|
||||||
filter: { deleted_at: { _is_null: true } },
|
filter: { deleted_at: { _is_null: true } },
|
||||||
allow_aggregations: true,
|
allow_aggregations: true,
|
||||||
},
|
},
|
||||||
@ -964,6 +978,7 @@ export const createFormDataMock = {
|
|||||||
role: 'user',
|
role: 'user',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id', 'metadata'],
|
columns: ['id', 'metadata'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {},
|
filter: {},
|
||||||
check: {},
|
check: {},
|
||||||
},
|
},
|
||||||
@ -989,6 +1004,7 @@ export const createFormDataMock = {
|
|||||||
role: 'sdfsf',
|
role: 'sdfsf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['id', 'name', 'deleted_at'],
|
columns: ['id', 'name', 'deleted_at'],
|
||||||
|
computed_fields: [],
|
||||||
filter: { _or: [] },
|
filter: { _or: [] },
|
||||||
query_root_fields: ['select', 'select_by_pk'],
|
query_root_fields: ['select', 'select_by_pk'],
|
||||||
subscription_root_fields: ['select', 'select_by_pk'],
|
subscription_root_fields: ['select', 'select_by_pk'],
|
||||||
@ -998,6 +1014,7 @@ export const createFormDataMock = {
|
|||||||
role: 'user',
|
role: 'user',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['deleted_at', 'id', 'name'],
|
columns: ['deleted_at', 'id', 'name'],
|
||||||
|
computed_fields: [],
|
||||||
filter: { deleted_at: { _is_null: true } },
|
filter: { deleted_at: { _is_null: true } },
|
||||||
query_root_fields: ['select', 'select_by_pk'],
|
query_root_fields: ['select', 'select_by_pk'],
|
||||||
subscription_root_fields: ['select', 'select_by_pk'],
|
subscription_root_fields: ['select', 'select_by_pk'],
|
||||||
@ -1049,6 +1066,7 @@ export const createFormDataMock = {
|
|||||||
role: 'asdf',
|
role: 'asdf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['AlbumId'],
|
columns: ['AlbumId'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_or: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
|
_or: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
|
||||||
},
|
},
|
||||||
@ -1058,6 +1076,7 @@ export const createFormDataMock = {
|
|||||||
role: 'new',
|
role: 'new',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['AlbumId'],
|
columns: ['AlbumId'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_or: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
|
_or: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
|
||||||
},
|
},
|
||||||
@ -1067,6 +1086,7 @@ export const createFormDataMock = {
|
|||||||
role: 'sdfsf',
|
role: 'sdfsf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['AlbumId', 'Title', 'ArtistId'],
|
columns: ['AlbumId', 'Title', 'ArtistId'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_and: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
|
_and: [{ AlbumId: { _eq: 'X-Hasura-User-Id' } }],
|
||||||
},
|
},
|
||||||
@ -1076,6 +1096,7 @@ export const createFormDataMock = {
|
|||||||
role: 'testrole',
|
role: 'testrole',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['AlbumId', 'Title'],
|
columns: ['AlbumId', 'Title'],
|
||||||
|
computed_fields: [],
|
||||||
filter: { _and: [{ AlbumId: { _eq: 'X-Hasura-User' } }] },
|
filter: { _and: [{ AlbumId: { _eq: 'X-Hasura-User' } }] },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1094,6 +1115,7 @@ export const createFormDataMock = {
|
|||||||
role: 'testrole',
|
role: 'testrole',
|
||||||
permission: {
|
permission: {
|
||||||
columns: [],
|
columns: [],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_exists: {
|
_exists: {
|
||||||
_table: ['Album'],
|
_table: ['Album'],
|
||||||
@ -1155,6 +1177,7 @@ export const createFormDataMock = {
|
|||||||
role: 'asdf',
|
role: 'asdf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: [],
|
columns: [],
|
||||||
|
computed_fields: [],
|
||||||
filter: { _not: { Data_value: { _eq: 1337 } } },
|
filter: { _not: { Data_value: { _eq: 1337 } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1162,6 +1185,7 @@ export const createFormDataMock = {
|
|||||||
role: 'new',
|
role: 'new',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['Series_reference', 'Period'],
|
columns: ['Series_reference', 'Period'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_and: [
|
_and: [
|
||||||
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
|
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
|
||||||
@ -1176,6 +1200,7 @@ export const createFormDataMock = {
|
|||||||
{
|
{
|
||||||
role: 'sdfsf',
|
role: 'sdfsf',
|
||||||
permission: {
|
permission: {
|
||||||
|
computed_fields: [],
|
||||||
columns: [
|
columns: [
|
||||||
'Series_reference',
|
'Series_reference',
|
||||||
'Period',
|
'Period',
|
||||||
@ -1201,6 +1226,7 @@ export const createFormDataMock = {
|
|||||||
role: 'testrole',
|
role: 'testrole',
|
||||||
permission: {
|
permission: {
|
||||||
columns: [],
|
columns: [],
|
||||||
|
computed_fields: [],
|
||||||
filter: { Magnitude: { _eq: '123' } },
|
filter: { Magnitude: { _eq: '123' } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1223,6 +1249,7 @@ export const createFormDataMock = {
|
|||||||
'Series_title_4',
|
'Series_title_4',
|
||||||
'Series_title_5',
|
'Series_title_5',
|
||||||
],
|
],
|
||||||
|
computed_fields: [],
|
||||||
filter: {},
|
filter: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1382,6 +1409,7 @@ export const createFormDataMock = {
|
|||||||
nullable: false,
|
nullable: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
computedFields: [],
|
||||||
trackedTables: [
|
trackedTables: [
|
||||||
{
|
{
|
||||||
table: { dataset: 'bigquery_sample', name: 'sample_table' },
|
table: { dataset: 'bigquery_sample', name: 'sample_table' },
|
||||||
@ -1425,6 +1453,7 @@ export const createFormDataMock = {
|
|||||||
role: 'new',
|
role: 'new',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['Series_reference', 'Period'],
|
columns: ['Series_reference', 'Period'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_and: [
|
_and: [
|
||||||
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
|
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
|
||||||
@ -1439,6 +1468,7 @@ export const createFormDataMock = {
|
|||||||
{
|
{
|
||||||
role: 'sdfsf',
|
role: 'sdfsf',
|
||||||
permission: {
|
permission: {
|
||||||
|
computed_fields: [],
|
||||||
columns: [
|
columns: [
|
||||||
'Series_reference',
|
'Series_reference',
|
||||||
'Period',
|
'Period',
|
||||||
@ -1462,11 +1492,16 @@ export const createFormDataMock = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'testrole',
|
role: 'testrole',
|
||||||
permission: { columns: [], filter: { Magnitude: { _eq: '123' } } },
|
permission: {
|
||||||
|
columns: [],
|
||||||
|
computed_fields: [],
|
||||||
|
filter: { Magnitude: { _eq: '123' } },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'user',
|
role: 'user',
|
||||||
permission: {
|
permission: {
|
||||||
|
computed_fields: [],
|
||||||
columns: [
|
columns: [
|
||||||
'Series_reference',
|
'Series_reference',
|
||||||
'Period',
|
'Period',
|
||||||
@ -1528,6 +1563,7 @@ export const createFormDataMock = {
|
|||||||
role: 'asdf',
|
role: 'asdf',
|
||||||
permission: {
|
permission: {
|
||||||
columns: [],
|
columns: [],
|
||||||
|
computed_fields: [],
|
||||||
filter: { _not: { Data_value: { _eq: 1337 } } },
|
filter: { _not: { Data_value: { _eq: 1337 } } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1535,6 +1571,7 @@ export const createFormDataMock = {
|
|||||||
role: 'new',
|
role: 'new',
|
||||||
permission: {
|
permission: {
|
||||||
columns: ['Series_reference', 'Period'],
|
columns: ['Series_reference', 'Period'],
|
||||||
|
computed_fields: [],
|
||||||
filter: {
|
filter: {
|
||||||
_and: [
|
_and: [
|
||||||
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
|
{ Data_value: { _eq: 'X-Hasura-User-Id' } },
|
||||||
@ -1549,6 +1586,7 @@ export const createFormDataMock = {
|
|||||||
{
|
{
|
||||||
role: 'sdfsf',
|
role: 'sdfsf',
|
||||||
permission: {
|
permission: {
|
||||||
|
computed_fields: [],
|
||||||
columns: [
|
columns: [
|
||||||
'Series_reference',
|
'Series_reference',
|
||||||
'Period',
|
'Period',
|
||||||
@ -1572,11 +1610,16 @@ export const createFormDataMock = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'testrole',
|
role: 'testrole',
|
||||||
permission: { columns: [], filter: { Magnitude: { _eq: '123' } } },
|
permission: {
|
||||||
|
columns: [],
|
||||||
|
computed_fields: [],
|
||||||
|
filter: { Magnitude: { _eq: '123' } },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'user',
|
role: 'user',
|
||||||
permission: {
|
permission: {
|
||||||
|
computed_fields: [],
|
||||||
columns: [
|
columns: [
|
||||||
'Series_reference',
|
'Series_reference',
|
||||||
'Period',
|
'Period',
|
||||||
|
@ -22,6 +22,7 @@ const formDataMockResult = {
|
|||||||
'Series_title_4',
|
'Series_title_4',
|
||||||
'Series_title_5',
|
'Series_title_5',
|
||||||
],
|
],
|
||||||
|
computed_fields: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
test('returns correctly formatted formData', () => {
|
test('returns correctly formatted formData', () => {
|
||||||
@ -79,6 +80,7 @@ const defaultValuesMockResult: ReturnType<typeof createDefaultValues> = {
|
|||||||
},
|
},
|
||||||
query_root_fields: null,
|
query_root_fields: null,
|
||||||
subscription_root_fields: null,
|
subscription_root_fields: null,
|
||||||
|
computed_fields: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
test('use default values returns values correctly', () => {
|
test('use default values returns values correctly', () => {
|
||||||
|
@ -11,6 +11,8 @@ export const selectArgs: CreateInsertArgs = {
|
|||||||
filterType: 'custom',
|
filterType: 'custom',
|
||||||
filter: { _not: { AlbumId: { _eq: 'X-Hasura-User-Id' } } },
|
filter: { _not: { AlbumId: { _eq: 'X-Hasura-User-Id' } } },
|
||||||
columns: { AlbumId: true, Title: true, ArtistId: true },
|
columns: { AlbumId: true, Title: true, ArtistId: true },
|
||||||
|
computed_fields: {},
|
||||||
|
comment: '',
|
||||||
presets: [],
|
presets: [],
|
||||||
rowCount: '0',
|
rowCount: '0',
|
||||||
aggregationEnabled: false,
|
aggregationEnabled: false,
|
||||||
@ -60,6 +62,7 @@ export const deleteArgs: CreateInsertArgs = {
|
|||||||
{ name: '<=', value: '_lte' },
|
{ name: '<=', value: '_lte' },
|
||||||
],
|
],
|
||||||
clonePermissions: [{ tableName: '', queryType: '', roleName: '' }],
|
clonePermissions: [{ tableName: '', queryType: '', roleName: '' }],
|
||||||
|
comment: '',
|
||||||
},
|
},
|
||||||
accessType: 'partialAccess',
|
accessType: 'partialAccess',
|
||||||
existingPermissions: [
|
existingPermissions: [
|
||||||
@ -94,6 +97,8 @@ export const insertArgs: CreateInsertArgs = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
columns: { AlbumId: false, Title: false, ArtistId: false },
|
columns: { AlbumId: false, Title: false, ArtistId: false },
|
||||||
|
computed_fields: {},
|
||||||
|
comment: '',
|
||||||
presets: [{ columnName: 'default', presetType: 'static', columnValue: '' }],
|
presets: [{ columnName: 'default', presetType: 'static', columnValue: '' }],
|
||||||
backendOnly: false,
|
backendOnly: false,
|
||||||
supportedOperators: [
|
supportedOperators: [
|
||||||
|
@ -2,6 +2,7 @@ import * as z from 'zod';
|
|||||||
import { inputValidationSchema } from '../../../components/Services/Data/TablePermissions/InputValidation/InputValidation';
|
import { inputValidationSchema } from '../../../components/Services/Data/TablePermissions/InputValidation/InputValidation';
|
||||||
|
|
||||||
const columns = z.record(z.optional(z.boolean()));
|
const columns = z.record(z.optional(z.boolean()));
|
||||||
|
const computed_fields = z.record(z.optional(z.boolean()));
|
||||||
const presets = z.optional(
|
const presets = z.optional(
|
||||||
z.array(
|
z.array(
|
||||||
z.object({
|
z.object({
|
||||||
@ -40,6 +41,7 @@ export const schema = z.discriminatedUnion('queryType', [
|
|||||||
comment: z.string(),
|
comment: z.string(),
|
||||||
check: z.any(),
|
check: z.any(),
|
||||||
columns,
|
columns,
|
||||||
|
computed_fields,
|
||||||
presets,
|
presets,
|
||||||
backendOnly: z.boolean().optional(),
|
backendOnly: z.boolean().optional(),
|
||||||
supportedOperators: z.array(z.any()),
|
supportedOperators: z.array(z.any()),
|
||||||
@ -52,6 +54,7 @@ export const schema = z.discriminatedUnion('queryType', [
|
|||||||
comment: z.string(),
|
comment: z.string(),
|
||||||
filter: z.any(),
|
filter: z.any(),
|
||||||
columns,
|
columns,
|
||||||
|
computed_fields,
|
||||||
presets,
|
presets,
|
||||||
rowCount: z.string().optional(),
|
rowCount: z.string().optional(),
|
||||||
aggregationEnabled: z.boolean().optional(),
|
aggregationEnabled: z.boolean().optional(),
|
||||||
@ -64,6 +67,7 @@ export const schema = z.discriminatedUnion('queryType', [
|
|||||||
z.object({
|
z.object({
|
||||||
queryType: z.literal('update'),
|
queryType: z.literal('update'),
|
||||||
columns,
|
columns,
|
||||||
|
computed_fields,
|
||||||
filterType: z.string(),
|
filterType: z.string(),
|
||||||
comment: z.string(),
|
comment: z.string(),
|
||||||
filter: z.any(),
|
filter: z.any(),
|
||||||
|
@ -30,6 +30,7 @@ export interface SelectPermission extends BasePermission {
|
|||||||
}
|
}
|
||||||
export interface SelectPermissionDefinition {
|
export interface SelectPermissionDefinition {
|
||||||
columns?: string[];
|
columns?: string[];
|
||||||
|
computed_fields?: string[];
|
||||||
filter?: Record<string, unknown>;
|
filter?: Record<string, unknown>;
|
||||||
allow_aggregations?: boolean;
|
allow_aggregations?: boolean;
|
||||||
query_root_fields?: string[] | null;
|
query_root_fields?: string[] | null;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { QualifiedFunction } from '../../../metadata/types';
|
||||||
import {
|
import {
|
||||||
InsertPermission,
|
InsertPermission,
|
||||||
SelectPermission,
|
SelectPermission,
|
||||||
@ -91,4 +92,11 @@ export type MetadataTable = {
|
|||||||
apollo_federation_config?: {
|
apollo_federation_config?: {
|
||||||
enable: 'v1';
|
enable: 'v1';
|
||||||
} | null;
|
} | null;
|
||||||
|
|
||||||
|
computed_fields?: {
|
||||||
|
name: string;
|
||||||
|
definition: {
|
||||||
|
function: QualifiedFunction;
|
||||||
|
};
|
||||||
|
}[];
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user