mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
This commit is contained in:
parent
eacda7cad5
commit
ddf27c1768
@ -149,16 +149,10 @@
|
||||
display: initial !important;
|
||||
|
||||
.tableIcon, .functionIcon {
|
||||
//display: inline;
|
||||
margin-right: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.functionIcon {
|
||||
width: 12px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,7 +168,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.activeTable {
|
||||
.activeLink {
|
||||
a {
|
||||
// border-left: 4px solid #FFC627;
|
||||
color: #FD9540!important;
|
||||
|
@ -14,7 +14,9 @@ const WarningSymbol = ({
|
||||
return (
|
||||
<div className={styles.display_inline}>
|
||||
<OverlayTrigger placement={tooltipPlacement} overlay={tooltip}>
|
||||
<WarningIcon customStyle={customStyle} />
|
||||
<span>
|
||||
<WarningIcon customStyle={customStyle} />
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
);
|
||||
|
@ -37,9 +37,22 @@ export const isEqual = (value1, value2) => {
|
||||
|
||||
if (typeof value1 === typeof value2) {
|
||||
if (isArray(value1)) {
|
||||
// TODO
|
||||
} else if (isObject(value2)) {
|
||||
_isEqual = JSON.stringify(value1) === JSON.stringify(value2);
|
||||
} else if (isObject(value2)) {
|
||||
const value1Keys = Object.keys(value1);
|
||||
const value2Keys = Object.keys(value2);
|
||||
|
||||
if (value1Keys.length === value2Keys.length) {
|
||||
_isEqual = true;
|
||||
|
||||
for (let i = 0; i < value1Keys.length; i++) {
|
||||
const key = value1Keys[i];
|
||||
if (!isEqual(value1[key], value2[key])) {
|
||||
_isEqual = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_isEqual = value1 === value2;
|
||||
}
|
||||
@ -48,6 +61,54 @@ export const isEqual = (value1, value2) => {
|
||||
return _isEqual;
|
||||
};
|
||||
|
||||
export function isJsonString(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getAllJsonPaths(json, leafKeys = [], prefix = '') {
|
||||
const _paths = [];
|
||||
|
||||
const addPrefix = subPath => {
|
||||
return prefix + (prefix && subPath ? '.' : '') + subPath;
|
||||
};
|
||||
|
||||
const handleSubJson = (subJson, newPrefix) => {
|
||||
const subPaths = getAllJsonPaths(subJson, leafKeys, newPrefix);
|
||||
|
||||
subPaths.forEach(subPath => {
|
||||
_paths.push(subPath);
|
||||
});
|
||||
|
||||
if (!subPaths.length) {
|
||||
_paths.push(newPrefix);
|
||||
}
|
||||
};
|
||||
|
||||
if (isArray(json)) {
|
||||
json.forEach((subJson, i) => {
|
||||
handleSubJson(subJson, addPrefix(i.toString()));
|
||||
});
|
||||
} else if (isObject(json)) {
|
||||
Object.keys(json).forEach(key => {
|
||||
if (leafKeys.includes(key)) {
|
||||
_paths.push({ [addPrefix(key)]: json[key] });
|
||||
} else {
|
||||
handleSubJson(json[key], addPrefix(key));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
_paths.push(addPrefix(json));
|
||||
}
|
||||
|
||||
return _paths;
|
||||
}
|
||||
|
||||
// use browser confirm and prompt to get user confirmation for actions
|
||||
export const getConfirmation = (
|
||||
message = '',
|
||||
|
@ -1,10 +1,8 @@
|
||||
import React from 'react';
|
||||
import { isEqual } from './jsUtils';
|
||||
import { isEqual, isString } from './jsUtils';
|
||||
|
||||
/*** Table/View utils ***/
|
||||
|
||||
// TODO: figure out better pattern for overloading fns
|
||||
|
||||
export const getTableName = table => {
|
||||
return table.table_name;
|
||||
};
|
||||
@ -13,6 +11,7 @@ export const getTableSchema = table => {
|
||||
return table.table_schema;
|
||||
};
|
||||
|
||||
// TODO: figure out better pattern for overloading fns
|
||||
// tableName and tableNameWithSchema are either/or arguments
|
||||
export const generateTableDef = (
|
||||
tableName,
|
||||
@ -34,15 +33,12 @@ export const getTableDef = table => {
|
||||
return generateTableDef(getTableName(table), getTableSchema(table));
|
||||
};
|
||||
|
||||
// table and tableDef are either/or arguments
|
||||
export const getTableNameWithSchema = (
|
||||
table,
|
||||
wrapDoubleQuotes = true,
|
||||
tableDef = null
|
||||
) => {
|
||||
let _fullTableName;
|
||||
export const getQualifiedTableDef = tableDef => {
|
||||
return isString(tableDef) ? generateTableDef(tableDef) : tableDef;
|
||||
};
|
||||
|
||||
tableDef = tableDef || getTableDef(table);
|
||||
export const getTableNameWithSchema = (tableDef, wrapDoubleQuotes = false) => {
|
||||
let _fullTableName;
|
||||
|
||||
if (wrapDoubleQuotes) {
|
||||
_fullTableName =
|
||||
@ -69,14 +65,40 @@ export const findTable = (allTables, tableDef) => {
|
||||
return allTables.find(t => isEqual(getTableDef(t), tableDef));
|
||||
};
|
||||
|
||||
export const getSchemaTables = (allTables, tableSchema) => {
|
||||
return allTables.filter(t => getTableSchema(t) === tableSchema);
|
||||
};
|
||||
|
||||
export const getTrackedTables = tables => {
|
||||
return tables.filter(t => t.is_table_tracked);
|
||||
};
|
||||
|
||||
/*** Table/View column utils ***/
|
||||
|
||||
export const getTableColumns = table => {
|
||||
return table.columns;
|
||||
};
|
||||
|
||||
export const getColumnName = column => {
|
||||
return column.column_name;
|
||||
};
|
||||
|
||||
export const getTableColumnNames = table => {
|
||||
return getTableColumns(table).map(c => getColumnName(c));
|
||||
};
|
||||
|
||||
export const getTableColumn = (table, columnName) => {
|
||||
return getTableColumns(table).find(
|
||||
column => getColumnName(column) === columnName
|
||||
);
|
||||
};
|
||||
|
||||
export const getColumnType = column => {
|
||||
let _columnType = column.data_type;
|
||||
|
||||
if (_columnType === 'USER-DEFINED') {
|
||||
_columnType = column.udt_name;
|
||||
}
|
||||
|
||||
return _columnType;
|
||||
};
|
||||
|
||||
export const isColumnAutoIncrement = column => {
|
||||
const columnDefault = column.column_default;
|
||||
|
||||
@ -88,7 +110,79 @@ export const isColumnAutoIncrement = column => {
|
||||
);
|
||||
};
|
||||
|
||||
/*** Table/View relationship utils ***/
|
||||
|
||||
export const getTableRelationships = table => {
|
||||
return table.relationships;
|
||||
};
|
||||
|
||||
export const getRelationshipName = relationship => {
|
||||
return relationship.rel_name;
|
||||
};
|
||||
|
||||
export const getRelationshipDef = relationship => {
|
||||
return relationship.rel_def;
|
||||
};
|
||||
|
||||
export const getRelationshipType = relationship => {
|
||||
return relationship.rel_type;
|
||||
};
|
||||
|
||||
export const getTableRelationshipNames = table => {
|
||||
return getTableRelationships(table).map(r => getRelationshipName(r));
|
||||
};
|
||||
|
||||
export function getTableRelationship(table, relationshipName) {
|
||||
return getTableRelationships(table).find(
|
||||
relationship => getRelationshipName(relationship) === relationshipName
|
||||
);
|
||||
}
|
||||
|
||||
export function getRelationshipRefTable(table, relationship) {
|
||||
let _refTable = null;
|
||||
|
||||
const relationshipDef = getRelationshipDef(relationship);
|
||||
const relationshipType = getRelationshipType(relationship);
|
||||
|
||||
// if manual relationship
|
||||
if (relationshipDef.manual_configuration) {
|
||||
_refTable = relationshipDef.manual_configuration.remote_table;
|
||||
}
|
||||
|
||||
// if foreign-key based relationship
|
||||
if (relationshipDef.foreign_key_constraint_on) {
|
||||
// if array relationship
|
||||
if (relationshipType === 'array') {
|
||||
_refTable = relationshipDef.foreign_key_constraint_on.table;
|
||||
}
|
||||
|
||||
// if object relationship
|
||||
if (relationshipType === 'object') {
|
||||
const fkCol = relationshipDef.foreign_key_constraint_on;
|
||||
|
||||
for (let i = 0; i < table.foreign_key_constraints.length; i++) {
|
||||
const fkConstraint = table.foreign_key_constraints[i];
|
||||
const fkConstraintCol = Object.keys(fkConstraint.column_mapping)[0];
|
||||
if (fkCol === fkConstraintCol) {
|
||||
_refTable = generateTableDef(
|
||||
fkConstraint.ref_table,
|
||||
fkConstraint.ref_table_table_schema
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof _refTable === 'string') {
|
||||
_refTable = generateTableDef(_refTable);
|
||||
}
|
||||
|
||||
return _refTable;
|
||||
}
|
||||
|
||||
/*** Table/View permissions utils ***/
|
||||
|
||||
export const getTablePermissions = (table, role = null, action = null) => {
|
||||
let tablePermissions = table.permissions;
|
||||
|
||||
@ -112,3 +206,17 @@ export const getFunctionSchema = pgFunction => {
|
||||
export const getFunctionName = pgFunction => {
|
||||
return pgFunction.function_name;
|
||||
};
|
||||
|
||||
/*** Schema utils ***/
|
||||
|
||||
export const getSchemaName = schema => {
|
||||
return schema.schema_name;
|
||||
};
|
||||
|
||||
export const getSchemaTables = (allTables, tableSchema) => {
|
||||
return allTables.filter(t => getTableSchema(t) === tableSchema);
|
||||
};
|
||||
|
||||
export const getSchemaTableNames = (allTables, tableSchema) => {
|
||||
return getSchemaTables(allTables, tableSchema).map(t => getTableName(t));
|
||||
};
|
||||
|
@ -12,7 +12,6 @@ import { NotFoundError } from '../../Error/PageNotFound';
|
||||
const sectionPrefix = '/data';
|
||||
|
||||
const DataPageContainer = ({
|
||||
schema,
|
||||
currentSchema,
|
||||
schemaList,
|
||||
children,
|
||||
@ -81,12 +80,7 @@ const DataPageContainer = ({
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<DataSubSidebar
|
||||
location={location}
|
||||
schema={schema}
|
||||
currentSchema={currentSchema}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
<DataSubSidebar location={location} />
|
||||
</li>
|
||||
<li
|
||||
role="presentation"
|
||||
@ -117,7 +111,6 @@ const DataPageContainer = ({
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
schema: state.tables.allSchemas,
|
||||
schemaList: state.tables.schemaList,
|
||||
currentSchema: state.tables.currentSchema,
|
||||
};
|
||||
|
@ -33,7 +33,6 @@ const defaultPermissionsState = {
|
||||
limitEnabled: true,
|
||||
bulkSelect: [],
|
||||
applySamePermissions: [],
|
||||
tableSchemas: [],
|
||||
};
|
||||
|
||||
const defaultPresetsState = {
|
||||
|
@ -8,6 +8,7 @@ import GqlCompatibilityWarning from '../../Common/GqlCompatibilityWarning/GqlCom
|
||||
import {
|
||||
displayTableName,
|
||||
getFunctionName,
|
||||
getSchemaTables,
|
||||
getTableName,
|
||||
} from '../../Common/utils/pgUtils';
|
||||
import {
|
||||
@ -20,23 +21,11 @@ class DataSubSidebar extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.tableSearch = this.tableSearch.bind(this);
|
||||
this.state = {
|
||||
trackedTables: [],
|
||||
searchInput: '',
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(props) {
|
||||
const { currentSchema, schema } = props;
|
||||
|
||||
const trackedTables = schema.filter(
|
||||
table => table.is_table_tracked && table.table_schema === currentSchema
|
||||
);
|
||||
|
||||
return {
|
||||
trackedTables: trackedTables,
|
||||
};
|
||||
this.tableSearch = this.tableSearch.bind(this);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
@ -60,23 +49,27 @@ class DataSubSidebar extends React.Component {
|
||||
const functionSymbol = require('../../Common/Layout/LeftSubSidebar/function.svg');
|
||||
const functionSymbolActive = require('../../Common/Layout/LeftSubSidebar/function_active.svg');
|
||||
const {
|
||||
functionsList,
|
||||
currentTable,
|
||||
currentSchema,
|
||||
migrationMode,
|
||||
location,
|
||||
currentFunction,
|
||||
trackedFunctions,
|
||||
allSchemas,
|
||||
} = this.props;
|
||||
|
||||
const { trackedTables, searchInput } = this.state;
|
||||
const { searchInput } = this.state;
|
||||
|
||||
const trackedTablesLength = trackedTables.length;
|
||||
const trackedTablesInSchema = getSchemaTables(
|
||||
allSchemas,
|
||||
currentSchema
|
||||
).filter(table => table.is_table_tracked);
|
||||
|
||||
const tableList = trackedTables.filter(t =>
|
||||
const filteredTableList = trackedTablesInSchema.filter(t =>
|
||||
getTableName(t).includes(searchInput)
|
||||
);
|
||||
|
||||
const listedFunctions = functionsList.filter(f =>
|
||||
const filteredFunctionsList = trackedFunctions.filter(f =>
|
||||
getFunctionName(f).includes(searchInput)
|
||||
);
|
||||
|
||||
@ -93,57 +86,56 @@ class DataSubSidebar extends React.Component {
|
||||
};
|
||||
|
||||
const getChildList = () => {
|
||||
let tableLinks = [
|
||||
<li className={styles.noChildren} key="no-tables-1">
|
||||
<i>No tables/views available</i>
|
||||
</li>,
|
||||
];
|
||||
|
||||
const tables = {};
|
||||
tableList.map(t => {
|
||||
if (t.is_table_tracked) {
|
||||
tables[getTableName(t)] = t;
|
||||
}
|
||||
});
|
||||
let childList;
|
||||
|
||||
const currentLocation = location.pathname;
|
||||
|
||||
if (tableList && tableList.length) {
|
||||
tableLinks = Object.keys(tables)
|
||||
.sort()
|
||||
.map((tableName, i) => {
|
||||
const table = tables[tableName];
|
||||
let tableLinks;
|
||||
if (filteredTableList && filteredTableList.length) {
|
||||
const filteredTablesObject = {};
|
||||
filteredTableList.forEach(t => {
|
||||
filteredTablesObject[getTableName(t)] = t;
|
||||
});
|
||||
|
||||
let activeTableClass = '';
|
||||
if (
|
||||
tableName === currentTable &&
|
||||
currentLocation.indexOf(currentTable) !== -1
|
||||
) {
|
||||
activeTableClass = styles.activeTable;
|
||||
}
|
||||
const sortedTableNames = Object.keys(filteredTablesObject).sort();
|
||||
|
||||
let gqlCompatibilityWarning = null;
|
||||
if (!gqlPattern.test(tableName)) {
|
||||
gqlCompatibilityWarning = (
|
||||
<span className={styles.add_mar_left_mid}>
|
||||
<GqlCompatibilityWarning />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
tableLinks = sortedTableNames.map((tableName, i) => {
|
||||
const table = filteredTablesObject[tableName];
|
||||
|
||||
return (
|
||||
<li className={activeTableClass} key={i}>
|
||||
<Link to={getTableBrowseRoute(table)} data-test={tableName}>
|
||||
<i
|
||||
className={styles.tableIcon + ' fa fa-table'}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{displayTableName(table)}
|
||||
</Link>
|
||||
{gqlCompatibilityWarning}
|
||||
</li>
|
||||
const isActive =
|
||||
tableName === currentTable && currentLocation.includes(tableName);
|
||||
|
||||
let gqlCompatibilityWarning = null;
|
||||
if (!gqlPattern.test(tableName)) {
|
||||
gqlCompatibilityWarning = (
|
||||
<span className={styles.add_mar_left_mid}>
|
||||
<GqlCompatibilityWarning />
|
||||
</span>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
className={isActive ? styles.activeLink : ''}
|
||||
key={'table ' + i}
|
||||
>
|
||||
<Link to={getTableBrowseRoute(table)} data-test={tableName}>
|
||||
<i
|
||||
className={styles.tableIcon + ' fa fa-table'}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{displayTableName(table)}
|
||||
</Link>
|
||||
{gqlCompatibilityWarning}
|
||||
</li>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
tableLinks = [
|
||||
<li className={styles.noChildren} key="no-tables-1">
|
||||
<i>No tables/views available</i>
|
||||
</li>,
|
||||
];
|
||||
}
|
||||
|
||||
const dividerHr = [
|
||||
@ -152,48 +144,57 @@ class DataSubSidebar extends React.Component {
|
||||
</li>,
|
||||
];
|
||||
|
||||
// If the listedFunctions is non empty
|
||||
if (listedFunctions.length > 0) {
|
||||
const functionHtml = listedFunctions.map((func, i) => {
|
||||
const funcName = getFunctionName(func);
|
||||
const isActive = funcName === currentFunction;
|
||||
if (filteredFunctionsList && filteredFunctionsList.length > 0) {
|
||||
const filteredFunctionsObject = {};
|
||||
filteredFunctionsList.forEach(f => {
|
||||
filteredFunctionsObject[getFunctionName(f)] = f;
|
||||
});
|
||||
|
||||
const sortedFunctionNames = Object.keys(filteredFunctionsObject).sort();
|
||||
|
||||
const functionLinks = sortedFunctionNames.map((funcName, i) => {
|
||||
const func = filteredFunctionsObject[funcName];
|
||||
|
||||
const isActive =
|
||||
funcName === currentFunction && currentLocation.includes(funcName);
|
||||
|
||||
return (
|
||||
<li className={isActive ? styles.activeTable : ''} key={'fn ' + i}>
|
||||
<li className={isActive ? styles.activeLink : ''} key={'fn ' + i}>
|
||||
<Link to={getFunctionModifyRoute(func)} data-test={funcName}>
|
||||
<div
|
||||
className={styles.display_inline + ' ' + styles.functionIcon}
|
||||
>
|
||||
<img src={isActive ? functionSymbolActive : functionSymbol} />
|
||||
</div>
|
||||
{getFunctionName(func)}
|
||||
<img
|
||||
src={isActive ? functionSymbolActive : functionSymbol}
|
||||
className={styles.functionIcon}
|
||||
/>
|
||||
<span>{funcName}</span>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
tableLinks = [...tableLinks, ...dividerHr, ...functionHtml];
|
||||
childList = [...tableLinks, ...dividerHr, ...functionLinks];
|
||||
} else if (
|
||||
functionsList.length !== listedFunctions.length &&
|
||||
listedFunctions.length === 0
|
||||
trackedFunctions.length > 0 &&
|
||||
filteredFunctionsList.length === 0
|
||||
) {
|
||||
const noFunctionResult = [
|
||||
<li className={styles.noChildren}>
|
||||
const noFunctionsMsg = [
|
||||
<li className={styles.noChildren} key="no-fns-1">
|
||||
<i>No matching functions available</i>
|
||||
</li>,
|
||||
];
|
||||
|
||||
tableLinks = [...tableLinks, ...dividerHr, ...noFunctionResult];
|
||||
childList = [...tableLinks, ...dividerHr, ...noFunctionsMsg];
|
||||
} else {
|
||||
childList = [...tableLinks];
|
||||
}
|
||||
|
||||
return tableLinks;
|
||||
return childList;
|
||||
};
|
||||
|
||||
return (
|
||||
<LeftSubSidebar
|
||||
showAddBtn={migrationMode}
|
||||
searchInput={getSearchInput()}
|
||||
heading={`Tables (${trackedTablesLength})`}
|
||||
heading={`Tables (${trackedTablesInSchema.length})`}
|
||||
addLink={getSchemaAddTableRoute(currentSchema)}
|
||||
addLabel={'Add Table'}
|
||||
addTestString={'sidebar-add-table'}
|
||||
@ -207,10 +208,12 @@ class DataSubSidebar extends React.Component {
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
currentTable: state.tables.currentTable,
|
||||
migrationMode: state.main.migrationMode,
|
||||
functionsList: state.tables.trackedFunctions,
|
||||
trackedFunctions: state.tables.trackedFunctions,
|
||||
currentFunction: state.functions.functionName,
|
||||
allSchemas: state.tables.allSchemas,
|
||||
currentTable: state.tables.currentTable,
|
||||
currentSchema: state.tables.currentSchema,
|
||||
serverVersion: state.main.serverVersion ? state.main.serverVersion : '',
|
||||
metadata: state.metadata,
|
||||
};
|
||||
|
@ -262,7 +262,7 @@ class PermissionsSummary extends Component {
|
||||
...copyState,
|
||||
copyFromRole: role,
|
||||
copyFromTable: currTable
|
||||
? getTableNameWithSchema(null, false, currTable)
|
||||
? getTableNameWithSchema(currTable)
|
||||
: 'all',
|
||||
copyFromAction: currRole ? 'all' : currAction,
|
||||
},
|
||||
@ -803,7 +803,7 @@ class PermissionsSummary extends Component {
|
||||
const getFromTableOptions = () => {
|
||||
return currSchemaTrackedTables.map(table => {
|
||||
const tableName = getTableName(table);
|
||||
const tableValue = getTableNameWithSchema(table, false);
|
||||
const tableValue = getTableNameWithSchema(getTableDef(table));
|
||||
|
||||
return (
|
||||
<option key={tableValue} value={tableValue}>
|
||||
|
@ -46,7 +46,6 @@ import {
|
||||
|
||||
import {
|
||||
PERM_OPEN_EDIT,
|
||||
PERM_ADD_TABLE_SCHEMAS,
|
||||
PERM_SET_FILTER,
|
||||
PERM_SET_FILTER_SAME_AS,
|
||||
PERM_TOGGLE_COLUMN,
|
||||
@ -260,25 +259,12 @@ const modifyReducer = (tableName, schemas, modifyStateOrig, action) => {
|
||||
...modifyState,
|
||||
permissionsState: {
|
||||
...permState,
|
||||
tableSchemas: schemas,
|
||||
},
|
||||
prevPermissionState: {
|
||||
...permState,
|
||||
},
|
||||
};
|
||||
|
||||
case PERM_ADD_TABLE_SCHEMAS:
|
||||
return {
|
||||
...modifyState,
|
||||
permissionsState: {
|
||||
...modifyState.permissionsState,
|
||||
tableSchemas: [
|
||||
...modifyState.permissionsState.tableSchemas,
|
||||
...action.schemas,
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
case PERM_CLOSE_EDIT:
|
||||
return {
|
||||
...modifyState,
|
||||
|
@ -5,10 +5,6 @@ import {
|
||||
} from '../DataState';
|
||||
import { getEdForm, getIngForm } from '../utils';
|
||||
import { makeMigrationCall } from '../DataActions';
|
||||
import dataHeaders from '../Common/Headers';
|
||||
import { globalCookiePolicy } from '../../../../Endpoints';
|
||||
import requestAction from '../../../../utils/requestAction';
|
||||
import Endpoints from '../../../../Endpoints';
|
||||
import {
|
||||
findTable,
|
||||
generateTableDef,
|
||||
@ -21,7 +17,6 @@ import {
|
||||
getDropPermissionQuery,
|
||||
} from '../../../Common/utils/v1QueryUtils';
|
||||
|
||||
export const PERM_ADD_TABLE_SCHEMAS = 'ModifyTable/PERM_ADD_TABLE_SCHEMAS';
|
||||
export const PERM_OPEN_EDIT = 'ModifyTable/PERM_OPEN_EDIT';
|
||||
export const PERM_SET_FILTER = 'ModifyTable/PERM_SET_FILTER';
|
||||
export const PERM_SET_FILTER_SAME_AS = 'ModifyTable/PERM_SET_FILTER_SAME_AS';
|
||||
@ -177,45 +172,6 @@ const getBasePermissionsState = (tableSchema, role, query) => {
|
||||
return _permissions;
|
||||
};
|
||||
|
||||
const permAddTableSchemas = schemaNames => {
|
||||
return (dispatch, getState) => {
|
||||
const url = Endpoints.getSchema;
|
||||
const options = {
|
||||
credentials: globalCookiePolicy,
|
||||
method: 'POST',
|
||||
headers: dataHeaders(getState),
|
||||
body: JSON.stringify({
|
||||
type: 'select',
|
||||
args: {
|
||||
table: {
|
||||
name: 'hdb_table',
|
||||
schema: 'hdb_catalog',
|
||||
},
|
||||
columns: [
|
||||
'*.*',
|
||||
{
|
||||
name: 'columns',
|
||||
columns: ['*.*'],
|
||||
order_by: [{ column: 'column_name', type: 'asc', nulls: 'last' }],
|
||||
},
|
||||
],
|
||||
where: { table_schema: { $in: schemaNames } },
|
||||
order_by: [{ column: 'table_name', type: 'asc', nulls: 'last' }],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
return dispatch(requestAction(url, options)).then(
|
||||
data => {
|
||||
dispatch({ type: PERM_ADD_TABLE_SCHEMAS, schemas: data });
|
||||
},
|
||||
error => {
|
||||
console.error('Failed to load table schemas: ' + JSON.stringify(error));
|
||||
}
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const updatePermissionsState = (permissions, key, value) => {
|
||||
const _permissions = JSON.parse(JSON.stringify(permissions));
|
||||
|
||||
@ -801,7 +757,6 @@ const permChangePermissions = changeType => {
|
||||
|
||||
export {
|
||||
permChangeTypes,
|
||||
permAddTableSchemas,
|
||||
permOpenEdit,
|
||||
permSetFilter,
|
||||
permSetFilterSameAs,
|
||||
|
@ -3,17 +3,10 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import QueryBuilderJson from '../../../../Common/QueryBuilderJson/QueryBuilderJson';
|
||||
|
||||
import {
|
||||
addToPrefix,
|
||||
getRefTable,
|
||||
getTableColumnNames,
|
||||
getTableRelationshipNames,
|
||||
getTableRelationship,
|
||||
getTableDef,
|
||||
getTableSchema,
|
||||
getColumnType,
|
||||
isJsonString,
|
||||
getAllJsonPaths,
|
||||
isArrayBoolOperator,
|
||||
isBoolOperator,
|
||||
isArrayColumnOperator,
|
||||
@ -23,23 +16,50 @@ import {
|
||||
boolOperators,
|
||||
PGTypes,
|
||||
PGTypesOperators,
|
||||
existOperators,
|
||||
isExistOperator,
|
||||
TABLE_KEY,
|
||||
WHERE_KEY,
|
||||
} from './utils';
|
||||
|
||||
import QueryBuilderJson from '../../../../Common/QueryBuilderJson/QueryBuilderJson';
|
||||
import {
|
||||
findTable,
|
||||
generateTableDef,
|
||||
getSchemaName,
|
||||
getTrackedTables,
|
||||
getColumnType,
|
||||
getTableColumn,
|
||||
getRelationshipRefTable,
|
||||
getTableColumnNames,
|
||||
getTableRelationshipNames,
|
||||
getTableRelationship,
|
||||
getTableSchema,
|
||||
getQualifiedTableDef,
|
||||
getSchemaTableNames,
|
||||
} from '../../../../Common/utils/pgUtils';
|
||||
|
||||
import {
|
||||
isJsonString,
|
||||
getAllJsonPaths,
|
||||
isObject,
|
||||
} from '../../../../Common/utils/jsUtils';
|
||||
|
||||
import { EXISTS_PERMISSION_SUPPORT } from '../../../../../helpers/versionUtils';
|
||||
import globals from '../../../../../Globals';
|
||||
|
||||
class PermissionBuilder extends React.Component {
|
||||
static propTypes = {
|
||||
allTableSchemas: PropTypes.array.isRequired,
|
||||
schemaList: PropTypes.array.isRequired,
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
dispatchFuncSetFilter: PropTypes.func.isRequired,
|
||||
dispatchFuncAddTableSchemas: PropTypes.func.isRequired,
|
||||
loadSchemasFunc: PropTypes.func.isRequired,
|
||||
filter: PropTypes.string,
|
||||
tableName: PropTypes.string,
|
||||
schemaName: PropTypes.string,
|
||||
tableDef: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchMissingSchemas();
|
||||
this.loadMissingSchemas();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@ -49,50 +69,79 @@ class PermissionBuilder extends React.Component {
|
||||
this.props.filter !== prevProps.filter ||
|
||||
this.props.allTableSchemas.length !== prevProps.allTableSchemas.length
|
||||
) {
|
||||
this.fetchMissingSchemas();
|
||||
this.loadMissingSchemas();
|
||||
}
|
||||
}
|
||||
|
||||
fetchMissingSchemas() {
|
||||
const {
|
||||
dispatch,
|
||||
tableName,
|
||||
schemaName,
|
||||
dispatchFuncAddTableSchemas,
|
||||
filter,
|
||||
} = this.props;
|
||||
loadMissingSchemas(
|
||||
tableDef = this.props.tableDef,
|
||||
filter = this.props.filter
|
||||
) {
|
||||
const { loadSchemasFunc } = this.props;
|
||||
|
||||
const findMissingSchemas = (path, currTable) => {
|
||||
let _missingSchemas = [];
|
||||
|
||||
let value;
|
||||
if (isObject(path)) {
|
||||
value = Object.values(path)[0];
|
||||
path = Object.keys(path)[0];
|
||||
}
|
||||
|
||||
const getNewPath = newPath => {
|
||||
return value ? { [newPath]: value } : newPath;
|
||||
};
|
||||
|
||||
const pathSplit = path.split('.');
|
||||
|
||||
const operator = pathSplit[0];
|
||||
|
||||
if (isArrayBoolOperator(operator)) {
|
||||
const newPath = pathSplit.slice(2).join('.');
|
||||
const newPath = getNewPath(pathSplit.slice(2).join('.'));
|
||||
_missingSchemas = findMissingSchemas(newPath, currTable);
|
||||
} else if (isBoolOperator(operator)) {
|
||||
const newPath = pathSplit.slice(1).join('.');
|
||||
const newPath = getNewPath(pathSplit.slice(1).join('.'));
|
||||
_missingSchemas = findMissingSchemas(newPath, currTable);
|
||||
} else if (isExistOperator(operator)) {
|
||||
const existTableDef = getQualifiedTableDef(value[TABLE_KEY]);
|
||||
const existTableSchema = existTableDef.schema;
|
||||
|
||||
const existWhere = value[WHERE_KEY];
|
||||
|
||||
if (existTableSchema) {
|
||||
const { allTableSchemas } = this.props;
|
||||
|
||||
const allSchemaNames = allTableSchemas.map(t => getTableSchema(t));
|
||||
|
||||
if (!allSchemaNames.includes(existTableSchema)) {
|
||||
_missingSchemas.push(existTableSchema);
|
||||
}
|
||||
}
|
||||
|
||||
this.loadMissingSchemas(existTableDef, JSON.stringify(existWhere));
|
||||
} else if (isColumnOperator(operator)) {
|
||||
// no missing schemas
|
||||
} else {
|
||||
const { allTableSchemas } = this.props;
|
||||
|
||||
const tableSchema = getTableSchema(allTableSchemas, currTable);
|
||||
const tableRelationships = getTableRelationshipNames(tableSchema);
|
||||
let tableRelationshipNames = [];
|
||||
|
||||
if (tableRelationships.includes(operator)) {
|
||||
const rel = getTableRelationship(tableSchema, operator);
|
||||
const refTable = getRefTable(rel, tableSchema);
|
||||
const tableSchema = findTable(allTableSchemas, currTable);
|
||||
|
||||
const refTableSchema = getTableSchema(allTableSchemas, refTable);
|
||||
if (tableSchema) {
|
||||
tableRelationshipNames = getTableRelationshipNames(tableSchema);
|
||||
}
|
||||
|
||||
if (tableRelationshipNames.includes(operator)) {
|
||||
const relationship = getTableRelationship(tableSchema, operator);
|
||||
const refTable = getRelationshipRefTable(tableSchema, relationship);
|
||||
|
||||
const refTableSchema = findTable(allTableSchemas, refTable);
|
||||
if (!refTableSchema) {
|
||||
_missingSchemas.push(refTable.schema);
|
||||
}
|
||||
|
||||
const newPath = pathSplit.slice(1).join('.');
|
||||
const newPath = getNewPath(pathSplit.slice(1).join('.'));
|
||||
_missingSchemas.push(...findMissingSchemas(newPath, refTable));
|
||||
} else {
|
||||
// no missing schemas
|
||||
@ -102,21 +151,17 @@ class PermissionBuilder extends React.Component {
|
||||
return _missingSchemas;
|
||||
};
|
||||
|
||||
const table = getTableDef(tableName, schemaName);
|
||||
|
||||
const missingSchemas = [];
|
||||
const paths = getAllJsonPaths(JSON.parse(filter || '{}'));
|
||||
const paths = getAllJsonPaths(JSON.parse(filter || '{}'), existOperators);
|
||||
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const path = paths[i];
|
||||
|
||||
const subMissingSchemas = findMissingSchemas(path, table);
|
||||
paths.forEach(path => {
|
||||
const subMissingSchemas = findMissingSchemas(path, tableDef);
|
||||
|
||||
missingSchemas.push(...subMissingSchemas);
|
||||
}
|
||||
});
|
||||
|
||||
if (missingSchemas.length > 0) {
|
||||
dispatch(dispatchFuncAddTableSchemas(missingSchemas));
|
||||
loadSchemasFunc(missingSchemas);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,8 +180,8 @@ class PermissionBuilder extends React.Component {
|
||||
|
||||
/********************************/
|
||||
|
||||
const getFilter = (conditions, prefix, value = '') => {
|
||||
let _where = {};
|
||||
const getFilter = (defaultSchema, conditions, prefix, value = '') => {
|
||||
let _boolExp = {};
|
||||
|
||||
const getArrayBoolOperatorFilter = (
|
||||
operator,
|
||||
@ -157,6 +202,7 @@ class PermissionBuilder extends React.Component {
|
||||
|
||||
_filter[operator] = opConditions;
|
||||
_filter[operator][position] = getFilter(
|
||||
defaultSchema,
|
||||
opConditions[position],
|
||||
newPrefix,
|
||||
opValue
|
||||
@ -181,7 +227,12 @@ class PermissionBuilder extends React.Component {
|
||||
if (isLast) {
|
||||
_filter[operator] = {};
|
||||
} else {
|
||||
_filter[operator] = getFilter(opConditions, opPrefix, opValue);
|
||||
_filter[operator] = getFilter(
|
||||
defaultSchema,
|
||||
opConditions,
|
||||
opPrefix,
|
||||
opValue
|
||||
);
|
||||
}
|
||||
|
||||
return _filter;
|
||||
@ -221,6 +272,39 @@ class PermissionBuilder extends React.Component {
|
||||
return _filter;
|
||||
};
|
||||
|
||||
const getExistsOperatorFilter = (
|
||||
operator,
|
||||
opValue,
|
||||
opConditions,
|
||||
opPrefix,
|
||||
isLast
|
||||
) => {
|
||||
const _filter = {
|
||||
[operator]: opConditions,
|
||||
};
|
||||
|
||||
if (isLast) {
|
||||
_filter[operator] = {
|
||||
[TABLE_KEY]: generateTableDef('', defaultSchema),
|
||||
[WHERE_KEY]: {},
|
||||
};
|
||||
} else if (opPrefix === TABLE_KEY) {
|
||||
_filter[operator] = {
|
||||
[TABLE_KEY]: opValue,
|
||||
[WHERE_KEY]: {},
|
||||
};
|
||||
} else if (opPrefix === WHERE_KEY) {
|
||||
_filter[operator][WHERE_KEY] = getFilter(
|
||||
defaultSchema,
|
||||
opConditions[opPrefix],
|
||||
opValue.prefix,
|
||||
opValue.value
|
||||
);
|
||||
}
|
||||
|
||||
return _filter;
|
||||
};
|
||||
|
||||
const getColumnFilter = (
|
||||
operator,
|
||||
opValue,
|
||||
@ -233,7 +317,12 @@ class PermissionBuilder extends React.Component {
|
||||
if (isLast) {
|
||||
_filter[operator] = {};
|
||||
} else {
|
||||
_filter[operator] = getFilter(opConditions, opPrefix, opValue);
|
||||
_filter[operator] = getFilter(
|
||||
defaultSchema,
|
||||
opConditions,
|
||||
opPrefix,
|
||||
opValue
|
||||
);
|
||||
}
|
||||
|
||||
return _filter;
|
||||
@ -249,9 +338,9 @@ class PermissionBuilder extends React.Component {
|
||||
const opConditions = isLast ? null : conditions[operator];
|
||||
|
||||
if (operator === '') {
|
||||
// blank where
|
||||
// blank bool exp
|
||||
} else if (isArrayBoolOperator(operator)) {
|
||||
_where = getArrayBoolOperatorFilter(
|
||||
_boolExp = getArrayBoolOperatorFilter(
|
||||
operator,
|
||||
value,
|
||||
opConditions,
|
||||
@ -259,7 +348,7 @@ class PermissionBuilder extends React.Component {
|
||||
isLast
|
||||
);
|
||||
} else if (isBoolOperator(operator)) {
|
||||
_where = getBoolOperatorFilter(
|
||||
_boolExp = getBoolOperatorFilter(
|
||||
operator,
|
||||
value,
|
||||
opConditions,
|
||||
@ -267,7 +356,7 @@ class PermissionBuilder extends React.Component {
|
||||
isLast
|
||||
);
|
||||
} else if (isArrayColumnOperator(operator)) {
|
||||
_where = getArrayColumnOperatorFilter(
|
||||
_boolExp = getArrayColumnOperatorFilter(
|
||||
operator,
|
||||
value,
|
||||
opConditions,
|
||||
@ -275,9 +364,17 @@ class PermissionBuilder extends React.Component {
|
||||
isLast
|
||||
);
|
||||
} else if (isColumnOperator(operator)) {
|
||||
_where = getColumnOperatorFilter(operator, value);
|
||||
_boolExp = getColumnOperatorFilter(operator, value);
|
||||
} else if (isExistOperator(operator)) {
|
||||
_boolExp = getExistsOperatorFilter(
|
||||
operator,
|
||||
value,
|
||||
opConditions,
|
||||
newPrefix,
|
||||
isLast
|
||||
);
|
||||
} else {
|
||||
_where = getColumnFilter(
|
||||
_boolExp = getColumnFilter(
|
||||
operator,
|
||||
value,
|
||||
opConditions,
|
||||
@ -286,13 +383,14 @@ class PermissionBuilder extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
return _where;
|
||||
return _boolExp;
|
||||
};
|
||||
|
||||
const _dispatchFunc = data => {
|
||||
const { dispatch, filter, dispatchFuncSetFilter } = this.props;
|
||||
const { dispatch, filter, dispatchFuncSetFilter, tableDef } = this.props;
|
||||
|
||||
const newFilter = getFilter(
|
||||
tableDef.schema,
|
||||
JSON.parse(filter || '{}'),
|
||||
data.prefix,
|
||||
data.value
|
||||
@ -590,53 +688,157 @@ class PermissionBuilder extends React.Component {
|
||||
|
||||
const renderColumnExp = (
|
||||
dispatchFunc,
|
||||
column,
|
||||
columnName,
|
||||
expression,
|
||||
table,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
prefix
|
||||
) => {
|
||||
let tableColumns = [];
|
||||
let tableRelationships = [];
|
||||
let tableColumnNames = [];
|
||||
let tableRelationshipNames = [];
|
||||
let tableSchema;
|
||||
if (table) {
|
||||
tableSchema = getTableSchema(tableSchemas, table);
|
||||
tableColumns = getTableColumnNames(tableSchema);
|
||||
tableRelationships = getTableRelationshipNames(tableSchema);
|
||||
if (tableDef) {
|
||||
tableSchema = findTable(tableSchemas, tableDef);
|
||||
if (tableSchema) {
|
||||
tableColumnNames = getTableColumnNames(tableSchema);
|
||||
tableRelationshipNames = getTableRelationshipNames(tableSchema);
|
||||
}
|
||||
}
|
||||
|
||||
let _columnExp = '';
|
||||
if (tableRelationships.includes(column)) {
|
||||
const rel = getTableRelationship(tableSchema, column);
|
||||
const refTable = getRefTable(rel, tableSchema);
|
||||
if (tableRelationshipNames.includes(columnName)) {
|
||||
const relationship = getTableRelationship(tableSchema, columnName);
|
||||
const refTable = getRelationshipRefTable(tableSchema, relationship);
|
||||
|
||||
_columnExp = renderBoolExp(
|
||||
dispatchFunc,
|
||||
expression,
|
||||
refTable,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
prefix
|
||||
); // eslint-disable-line no-use-before-define
|
||||
);
|
||||
} else {
|
||||
const columnType = getColumnType(column, tableSchema);
|
||||
let columnType = '';
|
||||
if (tableSchema && columnName) {
|
||||
const column = getTableColumn(tableSchema, columnName);
|
||||
columnType = getColumnType(column);
|
||||
}
|
||||
|
||||
_columnExp = renderOperatorExp(
|
||||
dispatchFunc,
|
||||
expression,
|
||||
prefix,
|
||||
columnType,
|
||||
tableColumns
|
||||
tableColumnNames
|
||||
);
|
||||
}
|
||||
|
||||
return _columnExp;
|
||||
};
|
||||
|
||||
const renderTableSelect = (
|
||||
dispatchFunc,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
defaultSchema
|
||||
) => {
|
||||
const selectedSchema = tableDef ? tableDef.schema : defaultSchema;
|
||||
const selectedTable = tableDef ? tableDef.name : '';
|
||||
|
||||
const schemaSelectDispatchFunc = val => {
|
||||
dispatchFunc(generateTableDef('', val));
|
||||
};
|
||||
|
||||
const tableSelectDispatchFunc = val => {
|
||||
dispatchFunc(generateTableDef(val, selectedSchema));
|
||||
};
|
||||
|
||||
const tableNames = getSchemaTableNames(tableSchemas, selectedSchema);
|
||||
|
||||
const schemaNames = schemaList.map(s => getSchemaName(s));
|
||||
|
||||
const schemaSelect = wrapDoubleQuotes(
|
||||
renderSelect(schemaSelectDispatchFunc, selectedSchema, schemaNames)
|
||||
);
|
||||
|
||||
const tableSelect = wrapDoubleQuotes(
|
||||
renderSelect(tableSelectDispatchFunc, selectedTable, tableNames)
|
||||
);
|
||||
|
||||
const _tableExp = [
|
||||
{ key: 'schema', value: schemaSelect },
|
||||
{ key: 'table', value: tableSelect },
|
||||
];
|
||||
|
||||
return <QueryBuilderJson element={_tableExp} />;
|
||||
};
|
||||
|
||||
const renderExistsExp = (
|
||||
dispatchFunc,
|
||||
operation,
|
||||
expression,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
prefix
|
||||
) => {
|
||||
const dispatchTableSelect = val => {
|
||||
dispatchFunc({ prefix: addToPrefix(prefix, TABLE_KEY), value: val });
|
||||
};
|
||||
|
||||
const dispatchWhereOperatorSelect = val => {
|
||||
dispatchFunc({ prefix: addToPrefix(prefix, WHERE_KEY), value: val });
|
||||
};
|
||||
|
||||
const existsOpTable = getQualifiedTableDef(expression[TABLE_KEY]);
|
||||
const existsOpWhere = expression[WHERE_KEY];
|
||||
|
||||
const tableSelect = renderTableSelect(
|
||||
dispatchTableSelect,
|
||||
existsOpTable,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
tableDef.schema
|
||||
);
|
||||
|
||||
let whereSelect = {};
|
||||
if (existsOpTable) {
|
||||
whereSelect = renderBoolExp(
|
||||
dispatchWhereOperatorSelect,
|
||||
existsOpWhere,
|
||||
existsOpTable,
|
||||
tableSchemas,
|
||||
schemaList
|
||||
);
|
||||
}
|
||||
|
||||
const _existsArgsJsonObject = {
|
||||
[TABLE_KEY]: tableSelect,
|
||||
[WHERE_KEY]: whereSelect,
|
||||
};
|
||||
|
||||
const unselectedElements = [];
|
||||
if (!existsOpTable.name) {
|
||||
unselectedElements.push(WHERE_KEY);
|
||||
}
|
||||
|
||||
return (
|
||||
<QueryBuilderJson
|
||||
element={_existsArgsJsonObject}
|
||||
unselectedElements={unselectedElements}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderBoolExpArray = (
|
||||
dispatchFunc,
|
||||
expressions,
|
||||
table,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
prefix
|
||||
) => {
|
||||
const _boolExpArray = [];
|
||||
@ -645,10 +847,11 @@ class PermissionBuilder extends React.Component {
|
||||
const _boolExp = renderBoolExp(
|
||||
dispatchFunc,
|
||||
expression,
|
||||
table,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
addToPrefix(prefix, i)
|
||||
); // eslint-disable-line no-use-before-define
|
||||
);
|
||||
_boolExpArray.push(_boolExp);
|
||||
});
|
||||
|
||||
@ -665,8 +868,9 @@ class PermissionBuilder extends React.Component {
|
||||
const renderBoolExp = (
|
||||
dispatchFunc,
|
||||
expression,
|
||||
table,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
prefix = ''
|
||||
) => {
|
||||
const dispatchOperationSelect = val => {
|
||||
@ -678,19 +882,32 @@ class PermissionBuilder extends React.Component {
|
||||
operation = Object.keys(expression)[0];
|
||||
}
|
||||
|
||||
let tableColumns = [];
|
||||
let tableRelationships = [];
|
||||
if (table) {
|
||||
const tableSchema = getTableSchema(tableSchemas, table);
|
||||
tableColumns = getTableColumnNames(tableSchema);
|
||||
tableRelationships = getTableRelationshipNames(tableSchema);
|
||||
let tableColumnNames = [];
|
||||
let tableRelationshipNames = [];
|
||||
if (tableDef) {
|
||||
const tableSchema = findTable(tableSchemas, tableDef);
|
||||
if (tableSchema) {
|
||||
tableColumnNames = getTableColumnNames(tableSchema);
|
||||
tableRelationshipNames = getTableRelationshipNames(tableSchema);
|
||||
}
|
||||
}
|
||||
|
||||
const columnOptions = tableColumns.concat(tableRelationships);
|
||||
const columnOptions = tableColumnNames.concat(tableRelationshipNames);
|
||||
|
||||
const operatorOptions = boolOperators
|
||||
.concat(['---'])
|
||||
.concat(columnOptions);
|
||||
const existsSupported =
|
||||
globals.featuresCompatibility &&
|
||||
globals.featuresCompatibility[EXISTS_PERMISSION_SUPPORT];
|
||||
|
||||
let operatorOptions;
|
||||
if (existsSupported) {
|
||||
operatorOptions = boolOperators
|
||||
.concat(['---'])
|
||||
.concat(existOperators)
|
||||
.concat(['---'])
|
||||
.concat(columnOptions);
|
||||
} else {
|
||||
operatorOptions = boolOperators.concat(['---']).concat(columnOptions);
|
||||
}
|
||||
|
||||
const _boolExpKey = renderSelect(
|
||||
dispatchOperationSelect,
|
||||
@ -707,16 +924,28 @@ class PermissionBuilder extends React.Component {
|
||||
_boolExpValue = renderBoolExpArray(
|
||||
dispatchFunc,
|
||||
expression[operation],
|
||||
table,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
newPrefix
|
||||
);
|
||||
} else if (isBoolOperator(operation)) {
|
||||
_boolExpValue = renderBoolExp(
|
||||
dispatchFunc,
|
||||
expression[operation],
|
||||
table,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
newPrefix
|
||||
);
|
||||
} else if (isExistOperator(operation)) {
|
||||
_boolExpValue = renderExistsExp(
|
||||
dispatchFunc,
|
||||
operation,
|
||||
expression[operation],
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
newPrefix
|
||||
);
|
||||
} else {
|
||||
@ -724,8 +953,9 @@ class PermissionBuilder extends React.Component {
|
||||
dispatchFunc,
|
||||
operation,
|
||||
expression[operation],
|
||||
table,
|
||||
tableDef,
|
||||
tableSchemas,
|
||||
schemaList,
|
||||
newPrefix
|
||||
);
|
||||
}
|
||||
@ -749,15 +979,16 @@ class PermissionBuilder extends React.Component {
|
||||
/********************************/
|
||||
|
||||
const showPermissionBuilder = () => {
|
||||
const { tableName, schemaName, filter, allTableSchemas } = this.props;
|
||||
const { tableDef, filter, allTableSchemas, schemaList } = this.props;
|
||||
|
||||
const table = getTableDef(tableName, schemaName);
|
||||
const trackedTables = getTrackedTables(allTableSchemas);
|
||||
|
||||
return renderBoolExp(
|
||||
_dispatchFunc,
|
||||
JSON.parse(filter || '{}'),
|
||||
table,
|
||||
allTableSchemas
|
||||
tableDef,
|
||||
trackedTables,
|
||||
schemaList
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -216,7 +216,14 @@ export const boolOperators = Object.keys(boolOperatorsInfo);
|
||||
|
||||
const columnOperators = Object.keys(columnOperatorsInfo);
|
||||
|
||||
export const allOperators = boolOperators.concat(columnOperators);
|
||||
export const existOperators = ['_exists'];
|
||||
|
||||
export const allOperators = boolOperators
|
||||
.concat(columnOperators)
|
||||
.concat(existOperators);
|
||||
|
||||
export const TABLE_KEY = '_table';
|
||||
export const WHERE_KEY = '_where';
|
||||
|
||||
/* Util functions */
|
||||
|
||||
@ -224,6 +231,10 @@ export const isBoolOperator = operator => {
|
||||
return boolOperators.includes(operator);
|
||||
};
|
||||
|
||||
export const isExistOperator = operator => {
|
||||
return existOperators.includes(operator);
|
||||
};
|
||||
|
||||
export const isArrayBoolOperator = operator => {
|
||||
const arrayBoolOperators = Object.keys(boolOperatorsInfo).filter(
|
||||
op => boolOperatorsInfo[op].type === 'array'
|
||||
@ -284,147 +295,3 @@ export function addToPrefix(prefix, value) {
|
||||
|
||||
return _newPrefix;
|
||||
}
|
||||
|
||||
export function getTableSchema(allSchemas, table) {
|
||||
return allSchemas.find(
|
||||
tableSchema =>
|
||||
tableSchema.table_name === table.name &&
|
||||
tableSchema.table_schema === table.schema
|
||||
);
|
||||
}
|
||||
|
||||
export function getTableColumnNames(tableSchema) {
|
||||
if (!tableSchema) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return tableSchema.columns.map(c => c.column_name);
|
||||
}
|
||||
|
||||
export function getTableRelationshipNames(tableSchema) {
|
||||
if (!tableSchema) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return tableSchema.relationships.map(r => r.rel_name);
|
||||
}
|
||||
|
||||
export function getTableRelationship(tableSchema, relName) {
|
||||
if (!tableSchema) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const relIndex = getTableRelationshipNames(tableSchema).indexOf(relName);
|
||||
|
||||
return tableSchema.relationships[relIndex];
|
||||
}
|
||||
|
||||
export function getTableDef(tableName, schema) {
|
||||
return { name: tableName, schema: schema };
|
||||
}
|
||||
|
||||
export function getRefTable(rel, tableSchema) {
|
||||
let _refTable = null;
|
||||
|
||||
// if manual relationship
|
||||
if (rel.rel_def.manual_configuration) {
|
||||
_refTable = rel.rel_def.manual_configuration.remote_table;
|
||||
}
|
||||
|
||||
// if foreign-key based relationship
|
||||
if (rel.rel_def.foreign_key_constraint_on) {
|
||||
// if array relationship
|
||||
if (rel.rel_type === 'array') {
|
||||
_refTable = rel.rel_def.foreign_key_constraint_on.table;
|
||||
}
|
||||
|
||||
// if object relationship
|
||||
if (rel.rel_type === 'object') {
|
||||
const fkCol = rel.rel_def.foreign_key_constraint_on;
|
||||
|
||||
for (let i = 0; i < tableSchema.foreign_key_constraints.length; i++) {
|
||||
const fkConstraint = tableSchema.foreign_key_constraints[i];
|
||||
const fkConstraintCol = Object.keys(fkConstraint.column_mapping)[0];
|
||||
if (fkCol === fkConstraintCol) {
|
||||
_refTable = getTableDef(
|
||||
fkConstraint.ref_table,
|
||||
fkConstraint.ref_table_table_schema
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof _refTable === 'string') {
|
||||
_refTable = getTableDef(_refTable, 'public');
|
||||
}
|
||||
|
||||
return _refTable;
|
||||
}
|
||||
|
||||
export function getColumnType(columnName, tableSchema) {
|
||||
let _columnType = '';
|
||||
|
||||
if (!tableSchema || !columnName) {
|
||||
return _columnType;
|
||||
}
|
||||
|
||||
const columnSchema = tableSchema.columns.find(
|
||||
_columnSchema => _columnSchema.column_name === columnName
|
||||
);
|
||||
|
||||
if (columnSchema) {
|
||||
_columnType = columnSchema.data_type;
|
||||
|
||||
if (_columnType === 'USER-DEFINED') {
|
||||
_columnType = columnSchema.udt_name;
|
||||
}
|
||||
}
|
||||
|
||||
return _columnType;
|
||||
}
|
||||
|
||||
export function isJsonString(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getAllJsonPaths(json, prefix = '') {
|
||||
const _paths = [];
|
||||
|
||||
const addPrefix = subPath => {
|
||||
return prefix + (prefix && subPath ? '.' : '') + subPath;
|
||||
};
|
||||
|
||||
const handleSubJson = (subJson, newPrefix) => {
|
||||
const subPaths = getAllJsonPaths(subJson, newPrefix);
|
||||
|
||||
subPaths.forEach(subPath => {
|
||||
_paths.push(subPath);
|
||||
});
|
||||
|
||||
if (!subPaths.length) {
|
||||
_paths.push(newPrefix);
|
||||
}
|
||||
};
|
||||
|
||||
if (json instanceof Array) {
|
||||
json.forEach((subJson, i) => {
|
||||
handleSubJson(subJson, addPrefix(i.toString()));
|
||||
});
|
||||
} else if (json instanceof Object) {
|
||||
Object.keys(json).forEach(key => {
|
||||
handleSubJson(json[key], addPrefix(key));
|
||||
});
|
||||
} else {
|
||||
_paths.push(addPrefix(json));
|
||||
}
|
||||
|
||||
return _paths;
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import { RESET } from '../TableModify/ModifyActions';
|
||||
import {
|
||||
permChangeTypes,
|
||||
permOpenEdit,
|
||||
permAddTableSchemas,
|
||||
permSetFilter,
|
||||
permSetFilterSameAs,
|
||||
permToggleColumn,
|
||||
@ -41,7 +40,7 @@ import TableHeader from '../TableCommon/TableHeader';
|
||||
import CollapsibleToggle from '../../../Common/CollapsibleToggle/CollapsibleToggle';
|
||||
import EnhancedInput from '../../../Common/InputChecker/InputChecker';
|
||||
|
||||
import { setTable } from '../DataActions';
|
||||
import { setTable, updateSchemaInfo } from '../DataActions';
|
||||
import { getIngForm, getEdForm, escapeRegExp } from '../utils';
|
||||
import { allOperators, getLegacyOperator } from './PermissionBuilder/utils';
|
||||
import {
|
||||
@ -57,6 +56,7 @@ import { defaultPresetsState } from '../DataState';
|
||||
|
||||
import { NotFoundError } from '../../../Error/PageNotFound';
|
||||
import { getConfirmation } from '../../../Common/utils/jsUtils';
|
||||
import { generateTableDef } from '../../../Common/utils/pgUtils';
|
||||
|
||||
class Permissions extends Component {
|
||||
constructor() {
|
||||
@ -111,6 +111,7 @@ class Permissions extends Component {
|
||||
tableName,
|
||||
tableType,
|
||||
allSchemas,
|
||||
schemaList,
|
||||
ongoingRequest,
|
||||
lastError,
|
||||
lastFormError,
|
||||
@ -711,8 +712,9 @@ class Permissions extends Component {
|
||||
const dispatchFuncSetFilter = filter =>
|
||||
permSetFilter(JSON.parse(filter));
|
||||
|
||||
const dispatchFuncAddTableSchemas = schemaNames =>
|
||||
permAddTableSchemas(schemaNames);
|
||||
const loadSchemasFunc = schemaNames => {
|
||||
dispatch(updateSchemaInfo({ schemas: schemaNames }));
|
||||
};
|
||||
|
||||
const isUniqueFilter =
|
||||
filterString !== '' &&
|
||||
@ -753,10 +755,10 @@ class Permissions extends Component {
|
||||
_filterOptionsSection.push(
|
||||
<PermissionBuilder
|
||||
dispatchFuncSetFilter={dispatchFuncSetFilter}
|
||||
dispatchFuncAddTableSchemas={dispatchFuncAddTableSchemas}
|
||||
tableName={tableName}
|
||||
schemaName={currentSchema}
|
||||
allTableSchemas={permissionsState.tableSchemas}
|
||||
loadSchemasFunc={loadSchemasFunc}
|
||||
tableDef={generateTableDef(tableName, currentSchema)}
|
||||
allTableSchemas={allSchemas}
|
||||
schemaList={schemaList}
|
||||
filter={filterString}
|
||||
dispatch={dispatch}
|
||||
key={-4}
|
||||
@ -1811,6 +1813,7 @@ const mapStateToProps = (state, ownProps) => ({
|
||||
tableName: ownProps.params.table,
|
||||
tableType: ownProps.route.tableType,
|
||||
allSchemas: state.tables.allSchemas,
|
||||
schemaList: state.tables.schemaList,
|
||||
migrationMode: state.main.migrationMode,
|
||||
currentSchema: state.tables.currentSchema,
|
||||
serverVersion: state.main.serverVersion ? state.main.serverVersion : '',
|
||||
|
@ -67,7 +67,7 @@ const EventSubSidebar = ({
|
||||
trigger === currentTrigger &&
|
||||
currentLocation.indexOf(currentTrigger) !== -1
|
||||
) {
|
||||
activeTableClass = styles.activeTable;
|
||||
activeTableClass = styles.activeLink;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -51,7 +51,7 @@ const RemoteSchemaSubSidebar = ({
|
||||
d.name === viewRemoteSchema &&
|
||||
location.pathname.includes(viewRemoteSchema)
|
||||
) {
|
||||
activeTableClass = styles.activeTable;
|
||||
activeTableClass = styles.activeLink;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -5,6 +5,7 @@ export const RELOAD_METADATA_API_CHANGE = 'reloadMetaDataApiChange';
|
||||
export const REMOTE_SCHEMA_TIMEOUT_CONF_SUPPORT =
|
||||
'remoteSchemaTimeoutConfSupport';
|
||||
export const TABLE_ENUMS_SUPPORT = 'tableEnumsSupport';
|
||||
export const EXISTS_PERMISSION_SUPPORT = 'existsPermissionSupport';
|
||||
|
||||
// list of feature launch versions
|
||||
const featureLaunchVersions = {
|
||||
@ -13,6 +14,7 @@ const featureLaunchVersions = {
|
||||
[FT_JWT_ANALYZER]: 'v1.0.0-beta.3',
|
||||
[REMOTE_SCHEMA_TIMEOUT_CONF_SUPPORT]: 'v1.0.0-beta.5',
|
||||
[TABLE_ENUMS_SUPPORT]: 'v1.0.0-beta.6',
|
||||
[EXISTS_PERMISSION_SUPPORT]: 'v1.0.0-beta.7',
|
||||
};
|
||||
|
||||
export const getFeaturesCompatibility = serverVersion => {
|
||||
|
Loading…
Reference in New Issue
Block a user