mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
console: fix fallback dataype operators and filterable input
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8716 GitOrigin-RevId: cc91c31353f079b078e59e8065e4a70f1b7ba0d4
This commit is contained in:
parent
4ccfc3490b
commit
74fe1c3268
@ -1,6 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { rowPermissionsContext } from './RowPermissionsProvider';
|
||||
import { useOperators } from './utils/comparatorsFromSchema';
|
||||
import Select from 'react-select';
|
||||
|
||||
export const Comparator = ({
|
||||
comparator,
|
||||
@ -16,20 +17,35 @@ export const Comparator = ({
|
||||
const operators = useOperators({ path });
|
||||
|
||||
return (
|
||||
<select
|
||||
data-testid={comparatorLevelId}
|
||||
className="border border-gray-200 rounded-md p-2"
|
||||
value={comparator}
|
||||
onChange={e => {
|
||||
setKey({ path, key: e.target.value, type: 'comparator' });
|
||||
<Select
|
||||
inputId={`${comparatorLevelId}-select-value`}
|
||||
isSearchable
|
||||
aria-label={comparatorLevelId}
|
||||
components={{ DropdownIndicator: null }}
|
||||
options={operators.map(o => ({
|
||||
value: o.name,
|
||||
label: o.name,
|
||||
}))}
|
||||
onChange={option => {
|
||||
const { value } = option as { value: string };
|
||||
setKey({ path, key: value, type: 'comparator' });
|
||||
}}
|
||||
>
|
||||
<option value="">-</option>
|
||||
{operators.map((o, index) => (
|
||||
<option key={index} value={o.name}>
|
||||
{o.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
defaultValue={{
|
||||
value: comparator,
|
||||
label: comparator,
|
||||
}}
|
||||
value={{
|
||||
value: comparator,
|
||||
label: comparator,
|
||||
}}
|
||||
styles={{
|
||||
control: base => ({
|
||||
...base,
|
||||
border: 0,
|
||||
minHeight: 'auto',
|
||||
}),
|
||||
}}
|
||||
className="w-32 border border-gray-200 rounded-md"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -304,7 +304,10 @@ export const BooleanArrayType: ComponentStory<
|
||||
BooleanArrayType.play = async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByTestId('Author-operator')).toBeInTheDocument();
|
||||
expect(canvas.getByTestId('Author._ceq-comparator')).toBeInTheDocument();
|
||||
const element = await canvas.getByLabelText('Author._ceq-comparator');
|
||||
await expect(element.getAttribute('id')).toEqual(
|
||||
'Author._ceq-comparator-select-value'
|
||||
);
|
||||
expect(
|
||||
canvas.getByTestId('Author._ceq-column-comparator-entry')
|
||||
).toBeInTheDocument();
|
||||
@ -550,9 +553,9 @@ SetNotPermission.play = async ({ canvasElement }) => {
|
||||
|
||||
await userEvent.selectOptions(canvas.getByTestId('_not-operator'), 'Period');
|
||||
|
||||
await userEvent.selectOptions(
|
||||
canvas.getByTestId('_not.Period._eq-comparator'),
|
||||
'_neq'
|
||||
const element = await canvas.getByLabelText('_not.Period._eq-comparator');
|
||||
await expect(element.getAttribute('id')).toEqual(
|
||||
'_not.Period._eq-comparator-select-value'
|
||||
);
|
||||
};
|
||||
|
||||
@ -641,9 +644,10 @@ JsonbColumns.play = async ({ canvasElement }) => {
|
||||
timeout: 50000,
|
||||
});
|
||||
// Expect jason._contained_in-comparator to be in the document
|
||||
expect(
|
||||
canvas.getByTestId('jason._contained_in-comparator')
|
||||
).toBeInTheDocument();
|
||||
const element = await canvas.getByLabelText('jason._contained_in-comparator');
|
||||
await expect(element.getAttribute('id')).toEqual(
|
||||
'jason._contained_in-comparator-select-value'
|
||||
);
|
||||
// Expect jason._contained_in-value-input to have value "{"a": "b"}"
|
||||
expect(canvas.getByTestId('jason._contained_in-value-input')).toHaveValue(
|
||||
'{"a":"b"}'
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,12 @@
|
||||
import { comparatorsFromSchema } from './comparatorsFromSchema';
|
||||
import {
|
||||
comparatorsFromSchema,
|
||||
getDataTypeOperators,
|
||||
mapScalarDataType,
|
||||
} from './comparatorsFromSchema';
|
||||
import { NamedTypeNode, parseType, typeFromAST } from 'graphql';
|
||||
import { schema } from '../__tests__/fixtures/graphql';
|
||||
import { SourceDataTypes } from './sourceDataTypes';
|
||||
import { mssqlRealColumnTypeInput } from '../__tests__/fixtures/getDataTypeOperators';
|
||||
|
||||
describe('comparatorsFromSchema', () => {
|
||||
it('should return comparators from schema', () => {
|
||||
@ -113,3 +119,20 @@ describe('comparatorsFromSchema', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDataTypeOperators', () => {
|
||||
it('should fallback to use int for integer type fallback columns', () => {
|
||||
const operators = getDataTypeOperators(mssqlRealColumnTypeInput);
|
||||
expect(operators.length).toEqual(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mapScalarDataType', () => {
|
||||
describe('MSSQL', () => {
|
||||
it('should return string for ntext type', () => {
|
||||
const dataType = mapScalarDataType('mssql', 'ntext' as SourceDataTypes);
|
||||
|
||||
expect(dataType).toEqual('string');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ import { areTablesEqual } from '../../../../../../hasura-metadata-api';
|
||||
import { Table } from '../../../../../../hasura-metadata-types';
|
||||
import { useContext } from 'react';
|
||||
import { rowPermissionsContext } from '../RowPermissionsProvider';
|
||||
import { sourceDataTypes, SourceDataTypes } from './sourceDataTypes';
|
||||
|
||||
function columnOperators(): Array<Operator> {
|
||||
return Object.keys(columnOperatorsInfo).reduce((acc, key) => {
|
||||
@ -144,9 +145,27 @@ const whitelist: Record<string, string[]> = {
|
||||
],
|
||||
};
|
||||
|
||||
type Sources =
|
||||
| 'postgres'
|
||||
| 'bigquery'
|
||||
| 'mssql'
|
||||
| 'citus'
|
||||
| 'cockroach'
|
||||
| 'alloy';
|
||||
|
||||
export const mapScalarDataType = (
|
||||
dataSource: string | undefined,
|
||||
dataType: SourceDataTypes
|
||||
) => {
|
||||
if (!dataSource) return dataType;
|
||||
const dataTypes = sourceDataTypes[dataSource as Sources];
|
||||
return dataTypes?.[dataType] || dataType;
|
||||
};
|
||||
|
||||
export function useOperators({ path }: { path: string[] }) {
|
||||
const { comparators, tables } = useContext(rowPermissionsContext);
|
||||
const { columns, table } = useContext(tableContext);
|
||||
|
||||
const columnName = path[path.length - 2];
|
||||
const column = columns.find(c => c.name === columnName);
|
||||
let dataType = column?.dataType;
|
||||
@ -168,19 +187,21 @@ export function useOperators({ path }: { path: string[] }) {
|
||||
return operators;
|
||||
}
|
||||
|
||||
function getDataTypeOperators({
|
||||
comparators,
|
||||
path,
|
||||
columns,
|
||||
tables,
|
||||
table,
|
||||
}: {
|
||||
export type GetDataTypeOperatorsProps = {
|
||||
comparators: Comparators;
|
||||
path: string[];
|
||||
columns: Columns;
|
||||
tables: Tables;
|
||||
table: Table;
|
||||
}) {
|
||||
};
|
||||
|
||||
export const getDataTypeOperators = ({
|
||||
comparators,
|
||||
path,
|
||||
columns,
|
||||
tables,
|
||||
table,
|
||||
}: GetDataTypeOperatorsProps) => {
|
||||
const columnName = path[path.length - 2];
|
||||
const column = columns.find(c => c.name === columnName);
|
||||
const dataSourceKind = tables.find(t => areTablesEqual(t.table, table))
|
||||
@ -191,10 +212,24 @@ function getDataTypeOperators({
|
||||
const comparatorKey = column ? `${column.dataType}${comparatorSuffix}` : '';
|
||||
const operators = comparators[comparatorKey]?.operators;
|
||||
if (!operators) {
|
||||
return allOperators;
|
||||
const dataSource = tables?.[0]?.dataSource?.name;
|
||||
const dataType = mapScalarDataType(
|
||||
dataSource,
|
||||
column?.dataType as SourceDataTypes
|
||||
);
|
||||
const fallbackComparatorKey = column
|
||||
? `${dataType}${comparatorSuffix}`
|
||||
: '';
|
||||
const lowerCaseComparators = Object.fromEntries(
|
||||
Object.entries(comparators).map(([k, v]) => [k.toLowerCase(), v])
|
||||
);
|
||||
const backupOperators =
|
||||
lowerCaseComparators[fallbackComparatorKey]?.operators;
|
||||
return backupOperators || allOperators;
|
||||
}
|
||||
|
||||
return operators;
|
||||
}
|
||||
};
|
||||
|
||||
function hasWhitelistedOperators(dataType: string) {
|
||||
return whitelist[dataType];
|
||||
|
@ -0,0 +1,257 @@
|
||||
export const sourceDataTypes = {
|
||||
mssql: {
|
||||
int: 'int',
|
||||
bigint: 'int',
|
||||
smallint: 'int',
|
||||
tinyint: 'int',
|
||||
bit: 'boolean',
|
||||
decimal: 'int',
|
||||
numeric: 'int',
|
||||
float: 'int',
|
||||
real: 'int',
|
||||
date: 'string',
|
||||
datetime: 'string',
|
||||
smalldatetime: 'string',
|
||||
datetime2: 'string',
|
||||
time: 'string',
|
||||
char: 'string',
|
||||
varchar: 'string',
|
||||
text: 'string',
|
||||
nchar: 'string',
|
||||
nvarchar: 'string',
|
||||
ntext: 'string',
|
||||
binary: 'string',
|
||||
varbinary: 'string',
|
||||
image: 'string',
|
||||
uniqueidentifier: 'string',
|
||||
},
|
||||
postgres: {
|
||||
integer: 'int',
|
||||
bigint: 'int',
|
||||
smallint: 'int',
|
||||
serial: 'int',
|
||||
bigserial: 'int',
|
||||
smallserial: 'int',
|
||||
numeric: 'int',
|
||||
decimal: 'int',
|
||||
real: 'int',
|
||||
boolean: 'boolean',
|
||||
char: 'string',
|
||||
varchar: 'string',
|
||||
'character varying': 'string',
|
||||
text: 'string',
|
||||
date: 'string',
|
||||
timestamp: 'string',
|
||||
timestamptz: 'string',
|
||||
time: 'string',
|
||||
timetz: 'string',
|
||||
interval: 'string',
|
||||
uuid: 'string',
|
||||
bytea: 'string',
|
||||
bit: 'string',
|
||||
json: 'object',
|
||||
jsonb: 'object',
|
||||
array: 'array',
|
||||
hstore: 'object',
|
||||
enum: 'string',
|
||||
tsvector: 'string',
|
||||
tsquery: 'string',
|
||||
inet: 'string',
|
||||
cidr: 'string',
|
||||
macaddr: 'string',
|
||||
macaddr8: 'string',
|
||||
point: 'object',
|
||||
line: 'object',
|
||||
lseg: 'object',
|
||||
box: 'object',
|
||||
path: 'object',
|
||||
polygon: 'object',
|
||||
circle: 'object',
|
||||
},
|
||||
mysql: {
|
||||
tinyint: 'integer',
|
||||
smallint: 'integer',
|
||||
mediumint: 'integer',
|
||||
int: 'integer',
|
||||
bigint: 'integer',
|
||||
bit: 'boolean',
|
||||
float: 'integer',
|
||||
double: 'integer',
|
||||
decimal: 'integer',
|
||||
numeric: 'integer',
|
||||
date: 'string',
|
||||
datetime: 'string',
|
||||
timestamp: 'string',
|
||||
time: 'string',
|
||||
year: 'integer',
|
||||
char: 'string',
|
||||
varchar: 'string',
|
||||
tinytext: 'string',
|
||||
text: 'string',
|
||||
mediumtext: 'string',
|
||||
longtext: 'string',
|
||||
binary: 'string',
|
||||
varbinary: 'string',
|
||||
tinyblob: 'string',
|
||||
blob: 'string',
|
||||
mediumblob: 'string',
|
||||
longblob: 'string',
|
||||
enum: 'string',
|
||||
set: 'string',
|
||||
json: 'object',
|
||||
geometry: 'object',
|
||||
point: 'object',
|
||||
linestring: 'object',
|
||||
polygon: 'object',
|
||||
multipoint: 'object',
|
||||
multilinestring: 'object',
|
||||
multipolygon: 'object',
|
||||
geometrycollection: 'object',
|
||||
},
|
||||
citus: {
|
||||
integer: 'integer',
|
||||
bigint: 'integer',
|
||||
smallint: 'integer',
|
||||
serial: 'integer',
|
||||
bigserial: 'integer',
|
||||
smallserial: 'integer',
|
||||
numeric: 'integer',
|
||||
decimal: 'integer',
|
||||
real: 'integer',
|
||||
'double precision': 'integer',
|
||||
boolean: 'boolean',
|
||||
char: 'string',
|
||||
varchar: 'string',
|
||||
text: 'string',
|
||||
date: 'string',
|
||||
timestamp: 'string',
|
||||
timestamptz: 'string',
|
||||
time: 'string',
|
||||
timetz: 'string',
|
||||
interval: 'string',
|
||||
uuid: 'string',
|
||||
bytea: 'string',
|
||||
bit: 'string',
|
||||
'bit varying': 'string',
|
||||
json: 'object',
|
||||
jsonb: 'object',
|
||||
array: 'array',
|
||||
hstore: 'object',
|
||||
enum: 'string',
|
||||
tsvector: 'string',
|
||||
tsquery: 'string',
|
||||
inet: 'string',
|
||||
cidr: 'string',
|
||||
macaddr: 'string',
|
||||
macaddr8: 'string',
|
||||
point: 'object',
|
||||
line: 'object',
|
||||
lseg: 'object',
|
||||
box: 'object',
|
||||
path: 'object',
|
||||
polygon: 'object',
|
||||
circle: 'object',
|
||||
},
|
||||
alloy: {
|
||||
integer: 'integer',
|
||||
bigint: 'integer',
|
||||
smallint: 'integer',
|
||||
serial: 'integer',
|
||||
bigserial: 'integer',
|
||||
smallserial: 'integer',
|
||||
numeric: 'integer',
|
||||
decimal: 'integer',
|
||||
real: 'integer',
|
||||
boolean: 'boolean',
|
||||
char: 'string',
|
||||
varchar: 'string',
|
||||
text: 'string',
|
||||
date: 'string',
|
||||
timestamp: 'string',
|
||||
timestamptz: 'string',
|
||||
time: 'string',
|
||||
timetz: 'string',
|
||||
interval: 'string',
|
||||
uuid: 'string',
|
||||
bytea: 'string',
|
||||
bit: 'string',
|
||||
json: 'object',
|
||||
jsonb: 'object',
|
||||
array: 'array',
|
||||
hstore: 'object',
|
||||
enum: 'string',
|
||||
tsvector: 'string',
|
||||
tsquery: 'string',
|
||||
inet: 'string',
|
||||
cidr: 'string',
|
||||
macaddr: 'string',
|
||||
macaddr8: 'string',
|
||||
point: 'object',
|
||||
line: 'object',
|
||||
lseg: 'object',
|
||||
box: 'object',
|
||||
path: 'object',
|
||||
polygon: 'object',
|
||||
circle: 'object',
|
||||
},
|
||||
bigquery: {
|
||||
INT64: 'integer',
|
||||
FLOAT64: 'integer',
|
||||
NUMERIC: 'integer',
|
||||
BIGNUMERIC: 'integer',
|
||||
BOOL: 'boolean',
|
||||
STRING: 'string',
|
||||
BYTES: 'string',
|
||||
DATE: 'string',
|
||||
DATETIME: 'string',
|
||||
TIME: 'string',
|
||||
TIMESTAMP: 'string',
|
||||
GEOGRAPHY: 'string',
|
||||
},
|
||||
cockroach: {
|
||||
INT: 'integer',
|
||||
INTEGER: 'integer',
|
||||
SMALLINT: 'integer',
|
||||
BIGINT: 'integer',
|
||||
SERIAL: 'integer',
|
||||
SMALLSERIAL: 'integer',
|
||||
BIGSERIAL: 'integer',
|
||||
FLOAT: 'integer',
|
||||
REAL: 'integer',
|
||||
'DOUBLE PRECISION': 'integer',
|
||||
DECIMAL: 'integer',
|
||||
NUMERIC: 'integer',
|
||||
BOOL: 'boolean',
|
||||
BOOLEAN: 'boolean',
|
||||
CHAR: 'string',
|
||||
VARCHAR: 'string',
|
||||
STRING: 'string',
|
||||
TEXT: 'string',
|
||||
BYTES: 'string',
|
||||
DATE: 'string',
|
||||
TIME: 'string',
|
||||
TIMESTAMP: 'string',
|
||||
INTERVAL: 'string',
|
||||
UUID: 'string',
|
||||
INET: 'string',
|
||||
JSON: 'object',
|
||||
JSONB: 'object',
|
||||
ENUM: 'string',
|
||||
},
|
||||
};
|
||||
|
||||
export type MsSQLTypes = keyof (typeof sourceDataTypes)['mssql'];
|
||||
export type PostgresTypes = keyof (typeof sourceDataTypes)['postgres'];
|
||||
export type MySQLTypes = keyof (typeof sourceDataTypes)['mysql'];
|
||||
export type CitusTypes = keyof (typeof sourceDataTypes)['citus'];
|
||||
export type AlloyTypes = keyof (typeof sourceDataTypes)['alloy'];
|
||||
export type CockroachTypes = keyof (typeof sourceDataTypes)['cockroach'];
|
||||
export type BigQueryTypes = keyof (typeof sourceDataTypes)['bigquery'];
|
||||
|
||||
export type SourceDataTypes = MsSQLTypes &
|
||||
PostgresTypes &
|
||||
MySQLTypes &
|
||||
CitusTypes &
|
||||
AlloyTypes &
|
||||
CockroachTypes &
|
||||
BigQueryTypes;
|
Loading…
Reference in New Issue
Block a user