mirror of
https://github.com/hasura/graphql-engine.git
synced 2025-01-05 22:34:22 +03:00
This commit is contained in:
parent
a7a60c2dfe
commit
4293714519
@ -8,6 +8,7 @@
|
|||||||
(Add entries here in the order of: server, console, cli, docs, others)
|
(Add entries here in the order of: server, console, cli, docs, others)
|
||||||
|
|
||||||
- console: allow manual edit of column types and handle array data types (close #2544, #3335, #2583) (#4546)
|
- console: allow manual edit of column types and handle array data types (close #2544, #3335, #2583) (#4546)
|
||||||
|
- console: add the ability to delete a role in permissions summary page (close #3353) (#4987)
|
||||||
|
|
||||||
## `v1.3.0-beta.2`
|
## `v1.3.0-beta.2`
|
||||||
|
|
||||||
|
@ -532,6 +532,7 @@ const makeMigrationCall = (
|
|||||||
successMsg,
|
successMsg,
|
||||||
errorMsg,
|
errorMsg,
|
||||||
shouldSkipSchemaReload,
|
shouldSkipSchemaReload,
|
||||||
|
skipExecution = false,
|
||||||
isRetry
|
isRetry
|
||||||
) => {
|
) => {
|
||||||
const upQuery = {
|
const upQuery = {
|
||||||
@ -548,6 +549,7 @@ const makeMigrationCall = (
|
|||||||
name: sanitize(migrationName),
|
name: sanitize(migrationName),
|
||||||
up: upQuery.args,
|
up: upQuery.args,
|
||||||
down: downQuery.args,
|
down: downQuery.args,
|
||||||
|
skip_execution: skipExecution,
|
||||||
};
|
};
|
||||||
|
|
||||||
const currMigrationMode = getState().main.migrationMode;
|
const currMigrationMode = getState().main.migrationMode;
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
import React, { ReactElement } from 'react';
|
||||||
|
import styles from './PermissionsSummary.scss';
|
||||||
|
|
||||||
|
type HeaderContentProps = {
|
||||||
|
content: string;
|
||||||
|
actionButtons: Array<ReactElement>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const HeaderContent: React.FC<HeaderContentProps> = ({
|
||||||
|
content,
|
||||||
|
actionButtons,
|
||||||
|
}) => {
|
||||||
|
return actionButtons.length ? (
|
||||||
|
<div
|
||||||
|
className={`${styles.actionCell} ${styles.display_flex} ${styles.flex_space_between}`}
|
||||||
|
>
|
||||||
|
<div>{content}</div>
|
||||||
|
<div className={`${styles.tableHeaderActions} ${styles.display_flex}`}>
|
||||||
|
{actionButtons.map((actionButton, i) => (
|
||||||
|
<div key={`${content}-action-btn-${i}`}>{actionButton}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>{content}</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type HeaderProps = {
|
||||||
|
content: string;
|
||||||
|
selectable: boolean;
|
||||||
|
isSelected?: boolean;
|
||||||
|
onClick?: () => void;
|
||||||
|
actionButtons?: Array<ReactElement>;
|
||||||
|
key?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Header: React.FC<HeaderProps> = ({
|
||||||
|
content,
|
||||||
|
selectable,
|
||||||
|
isSelected,
|
||||||
|
onClick,
|
||||||
|
actionButtons = [],
|
||||||
|
key,
|
||||||
|
}) => {
|
||||||
|
const selectableClassName = selectable ? styles.cursorPointer : '';
|
||||||
|
const isSelectedClassName = isSelected ? styles.selected : '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<th
|
||||||
|
key={key || content}
|
||||||
|
onClick={selectable ? onClick : undefined}
|
||||||
|
className={`${selectableClassName} ${isSelectedClassName}`}
|
||||||
|
>
|
||||||
|
<HeaderContent content={content} actionButtons={actionButtons} />
|
||||||
|
</th>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
@ -23,7 +23,11 @@ import {
|
|||||||
import { getConfirmation } from '../../../Common/utils/jsUtils';
|
import { getConfirmation } from '../../../Common/utils/jsUtils';
|
||||||
|
|
||||||
import { updateSchemaInfo } from '../DataActions';
|
import { updateSchemaInfo } from '../DataActions';
|
||||||
import { copyRolePermissions, permOpenEdit } from '../TablePermissions/Actions';
|
import {
|
||||||
|
copyRolePermissions,
|
||||||
|
permOpenEdit,
|
||||||
|
deleteRoleGlobally,
|
||||||
|
} from '../TablePermissions/Actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getAllRoles,
|
getAllRoles,
|
||||||
@ -33,6 +37,9 @@ import {
|
|||||||
getPermissionRowAccessSummary,
|
getPermissionRowAccessSummary,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
|
import Header from './Header';
|
||||||
|
import RolesHeader from './RolesHeader';
|
||||||
|
|
||||||
class PermissionsSummary extends Component {
|
class PermissionsSummary extends Component {
|
||||||
initState = {
|
initState = {
|
||||||
currRole: null,
|
currRole: null,
|
||||||
@ -182,52 +189,6 @@ class PermissionsSummary extends Component {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHeader = (
|
|
||||||
content,
|
|
||||||
selectable,
|
|
||||||
isSelected,
|
|
||||||
onClick,
|
|
||||||
actionBtn = null,
|
|
||||||
key = null
|
|
||||||
) => {
|
|
||||||
const getContents = () => {
|
|
||||||
let headerContent;
|
|
||||||
|
|
||||||
if (!actionBtn) {
|
|
||||||
headerContent = content;
|
|
||||||
} else {
|
|
||||||
headerContent = (
|
|
||||||
<div
|
|
||||||
className={
|
|
||||||
styles.actionCell +
|
|
||||||
' ' +
|
|
||||||
styles.display_flex +
|
|
||||||
' ' +
|
|
||||||
styles.flex_space_between
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div>{content}</div>
|
|
||||||
<div>{actionBtn}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return headerContent;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<th
|
|
||||||
key={key || content}
|
|
||||||
onClick={selectable ? onClick : null}
|
|
||||||
className={`${selectable ? styles.cursorPointer : ''} ${
|
|
||||||
isSelected ? styles.selected : ''
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{getContents()}
|
|
||||||
</th>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCellOnClick = (table, role, action) => {
|
const getCellOnClick = (table, role, action) => {
|
||||||
return () => {
|
return () => {
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -247,66 +208,46 @@ class PermissionsSummary extends Component {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRolesHeaders = (selectable = true, selectedFirst = false) => {
|
const copyOnClick = (e, role) => {
|
||||||
const rolesHeaders = [];
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
if (!allRoles.length) {
|
this.setState({
|
||||||
rolesHeaders.push(getHeader('No roles', false));
|
copyState: {
|
||||||
} else {
|
...copyState,
|
||||||
allRoles.forEach(role => {
|
copyFromRole: role,
|
||||||
const isCurrRole = currRole === role;
|
copyFromTable: currTable ? getTableNameWithSchema(currTable) : 'all',
|
||||||
|
copyFromAction: currRole ? 'all' : currAction,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const setRole = () => {
|
const deleteOnClick = (e, role) => {
|
||||||
this.setState({ currRole: isCurrRole ? null : role });
|
e.preventDefault();
|
||||||
window.scrollTo(0, 0);
|
e.stopPropagation();
|
||||||
};
|
|
||||||
|
|
||||||
const getCopyBtn = () => {
|
const deleteConfirmed = getConfirmation(
|
||||||
const copyOnClick = e => {
|
`This will delete all permissions for the role: "${role}" for all entities in the current Postgres schema`,
|
||||||
e.preventDefault();
|
true,
|
||||||
e.stopPropagation();
|
role
|
||||||
|
);
|
||||||
|
|
||||||
this.setState({
|
if (deleteConfirmed) {
|
||||||
copyState: {
|
dispatch(deleteRoleGlobally(role));
|
||||||
...copyState,
|
|
||||||
copyFromRole: role,
|
|
||||||
copyFromTable: currTable
|
|
||||||
? getTableNameWithSchema(currTable)
|
|
||||||
: 'all',
|
|
||||||
copyFromAction: currRole ? 'all' : currAction,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
color="white"
|
|
||||||
size="xs"
|
|
||||||
onClick={copyOnClick}
|
|
||||||
title="Copy permissions"
|
|
||||||
>
|
|
||||||
{getActionIcon('fa-copy')}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const roleHeader = getHeader(
|
|
||||||
role,
|
|
||||||
selectable,
|
|
||||||
isCurrRole,
|
|
||||||
setRole,
|
|
||||||
getCopyBtn()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selectedFirst && isCurrRole) {
|
|
||||||
rolesHeaders.unshift(roleHeader);
|
|
||||||
} else {
|
|
||||||
rolesHeaders.push(roleHeader);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return rolesHeaders;
|
const setRole = (role, isCurrRole) => {
|
||||||
|
this.setState({ currRole: isCurrRole ? null : role });
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultRolesHeaderProps = {
|
||||||
|
allRoles,
|
||||||
|
currentRole: currRole,
|
||||||
|
onCopyClick: copyOnClick,
|
||||||
|
onDeleteClick: deleteOnClick,
|
||||||
|
setRole,
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRolesCells = (table, roleCellRenderer) => {
|
const getRolesCells = (table, roleCellRenderer) => {
|
||||||
@ -366,7 +307,9 @@ class PermissionsSummary extends Component {
|
|||||||
|
|
||||||
if (!currSchemaTrackedTables.length) {
|
if (!currSchemaTrackedTables.length) {
|
||||||
tablesRows.push(
|
tablesRows.push(
|
||||||
<tr key={'No tables'}>{getHeader('No tables', false)}</tr>
|
<tr key={'No tables'}>
|
||||||
|
<Header content="No tables" selectable={false} />
|
||||||
|
</tr>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
currSchemaTrackedTables.forEach((table, i) => {
|
currSchemaTrackedTables.forEach((table, i) => {
|
||||||
@ -385,13 +328,15 @@ class PermissionsSummary extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return getHeader(
|
return (
|
||||||
displayTableName(table),
|
<Header
|
||||||
selectable,
|
content={displayTableName(table)}
|
||||||
isCurrTable,
|
selectable={selectable}
|
||||||
setTable,
|
isSelected={isCurrTable}
|
||||||
null,
|
onClick={setTable}
|
||||||
tableName
|
actionButtons={[]}
|
||||||
|
key={tableName}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -631,7 +576,7 @@ class PermissionsSummary extends Component {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{getActionSelector()}
|
{getActionSelector()}
|
||||||
{getRolesHeaders(false)}
|
<RolesHeader selectable={false} {...defaultRolesHeaderProps} />
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -656,7 +601,11 @@ class PermissionsSummary extends Component {
|
|||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
{getBackBtn('currRole')}
|
{getBackBtn('currRole')}
|
||||||
{getRolesHeaders(true, true)}
|
<RolesHeader
|
||||||
|
selectable
|
||||||
|
selectedFirst
|
||||||
|
{...defaultRolesHeaderProps}
|
||||||
|
/>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -715,7 +664,7 @@ class PermissionsSummary extends Component {
|
|||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
{getActionSelector()}
|
{getActionSelector()}
|
||||||
{getRolesHeaders()}
|
<RolesHeader {...defaultRolesHeaderProps} />
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@import "../../../Common/Common";
|
@import '../../../Common/Common';
|
||||||
|
|
||||||
//.rolesTableWrapper {
|
//.rolesTableWrapper {
|
||||||
//}
|
//}
|
||||||
@ -6,7 +6,8 @@
|
|||||||
.rolesTable {
|
.rolesTable {
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
|
|
||||||
th, td {
|
th,
|
||||||
|
td {
|
||||||
vertical-align: middle !important;
|
vertical-align: middle !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,7 +26,7 @@
|
|||||||
|
|
||||||
.selected,
|
.selected,
|
||||||
.selected th {
|
.selected th {
|
||||||
background-color: #FFF3D5 !important;
|
background-color: #fff3d5 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.permissionSymbolNA {
|
.permissionSymbolNA {
|
||||||
@ -71,3 +72,8 @@
|
|||||||
width: calc(100% - 20px);
|
width: calc(100% - 20px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tableHeaderActions {
|
||||||
|
width: 60px;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Button from '../../../Common/Button/Button';
|
||||||
|
import Header from './Header';
|
||||||
|
import styles from './PermissionsSummary.scss';
|
||||||
|
|
||||||
|
type IconButtonProps = {
|
||||||
|
onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
|
icon: string;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const IconButton: React.FC<IconButtonProps> = ({ onClick, icon, title }) => (
|
||||||
|
<Button color="white" size="xs" onClick={onClick} title={title}>
|
||||||
|
<i className={`fa ${icon} ${styles.actionIcon}`} aria-hidden="true" />
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
type RolesHeaderProps = {
|
||||||
|
selectable?: boolean;
|
||||||
|
selectedFirst?: boolean;
|
||||||
|
allRoles: Array<string>;
|
||||||
|
currentRole: string;
|
||||||
|
onCopyClick: (e: React.MouseEvent<HTMLButtonElement>, role: string) => void;
|
||||||
|
onDeleteClick: (e: React.MouseEvent<HTMLButtonElement>, role: string) => void;
|
||||||
|
setRole: (role: string, isCurrRole: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RolesHeader: React.FC<RolesHeaderProps> = ({
|
||||||
|
selectable = true,
|
||||||
|
selectedFirst = false,
|
||||||
|
allRoles,
|
||||||
|
currentRole,
|
||||||
|
onCopyClick,
|
||||||
|
onDeleteClick,
|
||||||
|
setRole,
|
||||||
|
}) => {
|
||||||
|
let roles = [...allRoles];
|
||||||
|
if (selectedFirst) {
|
||||||
|
roles = allRoles.reduce((acc, role) => {
|
||||||
|
if (role === currentRole) return [role, ...acc];
|
||||||
|
return [...acc, role];
|
||||||
|
}, [] as string[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{roles.length ? (
|
||||||
|
roles.map(role => (
|
||||||
|
<Header
|
||||||
|
content={role}
|
||||||
|
selectable={selectable}
|
||||||
|
isSelected={currentRole === role}
|
||||||
|
onClick={() => setRole(role, currentRole === role)}
|
||||||
|
actionButtons={[
|
||||||
|
<IconButton
|
||||||
|
icon="fa-copy"
|
||||||
|
onClick={e => onCopyClick(e, role)}
|
||||||
|
title="Copy Permissions"
|
||||||
|
/>,
|
||||||
|
<IconButton
|
||||||
|
icon="fa-trash"
|
||||||
|
onClick={e => onDeleteClick(e, role)}
|
||||||
|
title="Delete Role Permissions"
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Header content="No roles" selectable={false} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RolesHeader;
|
@ -39,6 +39,107 @@ import { getConfirmation } from '../../../Common/utils/jsUtils';
|
|||||||
import ToolTip from '../../../Common/Tooltip/Tooltip';
|
import ToolTip from '../../../Common/Tooltip/Tooltip';
|
||||||
import KnowMoreLink from '../../../Common/KnowMoreLink/KnowMoreLink';
|
import KnowMoreLink from '../../../Common/KnowMoreLink/KnowMoreLink';
|
||||||
import RawSqlButton from '../Common/Components/RawSqlButton';
|
import RawSqlButton from '../Common/Components/RawSqlButton';
|
||||||
|
import styles from '../../../Common/Common.scss';
|
||||||
|
|
||||||
|
const SchemaPermissionsButton = ({ schema }) => (
|
||||||
|
<Link to={getSchemaPermissionsRoute(schema)} style={{ marginLeft: '20px' }}>
|
||||||
|
<Button color="white" size="xs" className={styles.add_mar_left_mid}>
|
||||||
|
Show Permissions Summary
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
const OpenCreateSection = React.forwardRef(
|
||||||
|
({ ref, value, handleInputChange, handleCreate, handleCancelCreate }) => (
|
||||||
|
<div className={styles.display_inline + ' ' + styles.add_mar_left}>
|
||||||
|
<div className={styles.display_inline}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={value}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="schema_name"
|
||||||
|
className={`form-control input-sm ${styles.display_inline}`}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
color="white"
|
||||||
|
size="xs"
|
||||||
|
onClick={handleCreate}
|
||||||
|
className={styles.add_mar_left_mid}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="white"
|
||||||
|
size="xs"
|
||||||
|
onClick={handleCancelCreate}
|
||||||
|
className={styles.add_mar_left_mid}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const ClosedCreateSection = ({ onClick }) => (
|
||||||
|
<Button color="white" size="xs" onClick={onClick} title="Create new schema">
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
const CreateSchemaSection = React.forwardRef(
|
||||||
|
({
|
||||||
|
ref,
|
||||||
|
schema,
|
||||||
|
migrationMode,
|
||||||
|
createSchemaOpen,
|
||||||
|
schemaNameEdit,
|
||||||
|
handleCancelCreateNewSchema,
|
||||||
|
handleCreateNewClick,
|
||||||
|
handleSchemaNameChange,
|
||||||
|
handleCreateClick,
|
||||||
|
}) =>
|
||||||
|
migrationMode && (
|
||||||
|
<div className={`${styles.display_flex}`}>
|
||||||
|
{createSchemaOpen ? (
|
||||||
|
<OpenCreateSection
|
||||||
|
ref={ref}
|
||||||
|
value={schemaNameEdit}
|
||||||
|
handleInputChange={handleSchemaNameChange}
|
||||||
|
handleCreate={handleCreateClick}
|
||||||
|
handleCancelCreate={handleCancelCreateNewSchema}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ClosedCreateSection onClick={handleCreateNewClick} />
|
||||||
|
)}
|
||||||
|
<SchemaPermissionsButton schema={schema} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const DeleteSchemaButton = ({ dispatch, migrationMode }) => {
|
||||||
|
const successCb = () => {
|
||||||
|
dispatch(updateCurrentSchema('public'));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
dispatch(deleteCurrentSchema(successCb));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
migrationMode && (
|
||||||
|
<Button
|
||||||
|
color="white"
|
||||||
|
size="xs"
|
||||||
|
onClick={handleDelete}
|
||||||
|
title="Delete current schema"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
class Schema extends Component {
|
class Schema extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -54,8 +155,44 @@ class Schema extends Component {
|
|||||||
this.props.dispatch(
|
this.props.dispatch(
|
||||||
updateSchemaInfo({ schemas: [this.props.currentSchema] })
|
updateSchemaInfo({ schemas: [this.props.currentSchema] })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.schemaNameInputRef = React.createRef(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelCreateNewSchema = () => {
|
||||||
|
this.setState({
|
||||||
|
createSchemaOpen: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onCreateNewClick = () => {
|
||||||
|
this.setState({ createSchemaOpen: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
onChangeSchemaName = e => {
|
||||||
|
this.setState({ schemaNameEdit: e.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCreateClick = () => {
|
||||||
|
const schemaName = this.state.schemaNameEdit.trim();
|
||||||
|
|
||||||
|
if (!schemaName) {
|
||||||
|
this.schemaNameInputRef.current.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const successCb = () => {
|
||||||
|
this.props.dispatch(updateCurrentSchema(schemaName));
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
schemaNameEdit: '',
|
||||||
|
createSchemaOpen: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.props.dispatch(createNewSchema(schemaName, successCb));
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
schema,
|
schema,
|
||||||
@ -70,14 +207,10 @@ class Schema extends Component {
|
|||||||
trackedFunctions,
|
trackedFunctions,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const styles = require('../../../Common/Common.scss');
|
|
||||||
|
|
||||||
const handleSchemaChange = e => {
|
const handleSchemaChange = e => {
|
||||||
dispatch(updateCurrentSchema(e.target.value));
|
dispatch(updateCurrentSchema(e.target.value));
|
||||||
};
|
};
|
||||||
|
|
||||||
/***********/
|
|
||||||
|
|
||||||
const _getTrackableFunctions = () => {
|
const _getTrackableFunctions = () => {
|
||||||
const trackedFuncNames = trackedFunctions.map(fn => getFunctionName(fn));
|
const trackedFuncNames = trackedFunctions.map(fn => getFunctionName(fn));
|
||||||
|
|
||||||
@ -104,8 +237,6 @@ class Schema extends Component {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/***********/
|
|
||||||
|
|
||||||
const allUntrackedTables = getUntrackedTables(
|
const allUntrackedTables = getUntrackedTables(
|
||||||
getSchemaTables(schema, currentSchema)
|
getSchemaTables(schema, currentSchema)
|
||||||
);
|
);
|
||||||
@ -144,146 +275,39 @@ class Schema extends Component {
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCreateSchemaSection = () => {
|
|
||||||
let createSchemaSection = null;
|
|
||||||
|
|
||||||
if (migrationMode) {
|
|
||||||
const { createSchemaOpen, schemaNameEdit } = this.state;
|
|
||||||
|
|
||||||
const handleCreateNewClick = () => {
|
|
||||||
this.setState({ createSchemaOpen: true });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSchemaNameChange = e => {
|
|
||||||
this.setState({ schemaNameEdit: e.target.value });
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCreateClick = () => {
|
|
||||||
const schemaName = schemaNameEdit.trim();
|
|
||||||
|
|
||||||
if (!schemaName) {
|
|
||||||
document.getElementById('schema-name-input').focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const successCb = () => {
|
|
||||||
dispatch(updateCurrentSchema(schemaName));
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
schemaNameEdit: '',
|
|
||||||
createSchemaOpen: false,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch(createNewSchema(schemaName, successCb));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCancelCreateNewSchema = () => {
|
|
||||||
this.setState({
|
|
||||||
createSchemaOpen: false,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const closedCreateSection = (
|
|
||||||
<Button
|
|
||||||
color="white"
|
|
||||||
size="xs"
|
|
||||||
onClick={handleCreateNewClick}
|
|
||||||
title="Create new schema"
|
|
||||||
>
|
|
||||||
<i className="fa fa-plus" aria-hidden="true" />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
|
|
||||||
const openCreateSection = (
|
|
||||||
<div className={styles.display_inline + ' ' + styles.add_mar_left}>
|
|
||||||
<div className={styles.display_inline}>
|
|
||||||
<input
|
|
||||||
id="schema-name-input"
|
|
||||||
type="text"
|
|
||||||
value={schemaNameEdit}
|
|
||||||
onChange={handleSchemaNameChange}
|
|
||||||
placeholder="schema_name"
|
|
||||||
className={'form-control input-sm ' + styles.display_inline}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
color="white"
|
|
||||||
size="xs"
|
|
||||||
onClick={handleCreateClick}
|
|
||||||
className={styles.add_mar_left_mid}
|
|
||||||
>
|
|
||||||
Create
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
color="white"
|
|
||||||
size="xs"
|
|
||||||
onClick={handleCancelCreateNewSchema}
|
|
||||||
className={styles.add_mar_left_mid}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
createSchemaSection = createSchemaOpen
|
|
||||||
? openCreateSection
|
|
||||||
: closedCreateSection;
|
|
||||||
}
|
|
||||||
|
|
||||||
return createSchemaSection;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDeleteSchemaBtn = () => {
|
|
||||||
let deleteSchemaBtn = null;
|
|
||||||
|
|
||||||
if (migrationMode) {
|
|
||||||
const handleDelete = () => {
|
|
||||||
const successCb = () => {
|
|
||||||
dispatch(updateCurrentSchema('public'));
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch(deleteCurrentSchema(successCb));
|
|
||||||
};
|
|
||||||
|
|
||||||
deleteSchemaBtn = (
|
|
||||||
<Button
|
|
||||||
color="white"
|
|
||||||
size="xs"
|
|
||||||
onClick={handleDelete}
|
|
||||||
title="Delete current schema"
|
|
||||||
>
|
|
||||||
<i className="fa fa-trash" aria-hidden="true" />
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return deleteSchemaBtn;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.add_mar_top}>
|
<div className={styles.add_mar_top}>
|
||||||
<div className={styles.display_inline}>Current Postgres schema</div>
|
<div className={styles.display_inline}>Current Postgres schema</div>
|
||||||
<div className={styles.display_inline}>
|
<div className={styles.display_inline}>
|
||||||
<select
|
<select
|
||||||
onChange={handleSchemaChange}
|
onChange={handleSchemaChange}
|
||||||
className={
|
className={`${styles.add_mar_left_mid} ${styles.width_auto} form-control`}
|
||||||
styles.add_mar_left_mid +
|
|
||||||
' ' +
|
|
||||||
styles.width_auto +
|
|
||||||
' form-control'
|
|
||||||
}
|
|
||||||
value={currentSchema}
|
value={currentSchema}
|
||||||
>
|
>
|
||||||
{getSchemaOptions()}
|
{getSchemaOptions()}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.display_inline + ' ' + styles.add_mar_left}>
|
<div className={`${styles.display_inline} ${styles.add_mar_left}`}>
|
||||||
<div className={styles.display_inline}>{getDeleteSchemaBtn()}</div>
|
<div className={styles.display_inline}>
|
||||||
|
<DeleteSchemaButton
|
||||||
|
dispatch={dispatch}
|
||||||
|
migrationMode={migrationMode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`${styles.display_inline} ${styles.add_mar_left_mid}`}
|
className={`${styles.display_inline} ${styles.add_mar_left_mid}`}
|
||||||
>
|
>
|
||||||
{getCreateSchemaSection()}
|
<CreateSchemaSection
|
||||||
|
ref={this.schemaNameInputRef}
|
||||||
|
schema={currentSchema}
|
||||||
|
migrationMode={migrationMode}
|
||||||
|
schemaNameEdit={this.state.schemaNameEdit}
|
||||||
|
createSchemaOpen={this.state.createSchemaOpen}
|
||||||
|
handleCancelCreateNewSchema={this.cancelCreateNewSchema}
|
||||||
|
handleCreateNewClick={this.onCreateNewClick}
|
||||||
|
handleSchemaNameChange={this.onChangeSchemaName}
|
||||||
|
handleCreateClick={this.handleCreateClick}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -675,16 +699,6 @@ class Schema extends Component {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPermissionsSummaryLink = () => {
|
|
||||||
return (
|
|
||||||
<div className={styles.add_mar_top}>
|
|
||||||
<Link to={getSchemaPermissionsRoute(currentSchema)}>
|
|
||||||
Schema permissions summary
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`container-fluid ${styles.padd_left_remove} ${styles.padd_top}`}
|
className={`container-fluid ${styles.padd_left_remove} ${styles.padd_top}`}
|
||||||
@ -703,7 +717,6 @@ class Schema extends Component {
|
|||||||
{getUntrackedFunctionsSection()}
|
{getUntrackedFunctionsSection()}
|
||||||
{getNonTrackableFunctionsSection()}
|
{getNonTrackableFunctionsSection()}
|
||||||
<hr />
|
<hr />
|
||||||
{getPermissionsSummaryLink()}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -643,6 +643,73 @@ const copyRolePermissions = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteRoleGlobally = roleName => {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const permissionsUpQueries = [];
|
||||||
|
const permissionsDownQueries = [];
|
||||||
|
|
||||||
|
const allSchemas = getState().tables.allSchemas;
|
||||||
|
const currentSchema = getState().tables.currentSchema;
|
||||||
|
|
||||||
|
const tables = getSchemaTables(allSchemas, currentSchema);
|
||||||
|
|
||||||
|
tables.forEach(table => {
|
||||||
|
const tableDef = getTableDef(table);
|
||||||
|
|
||||||
|
const actions = ['select', 'insert', 'update', 'delete'];
|
||||||
|
|
||||||
|
actions.forEach(_action => {
|
||||||
|
const currPermissions = getTablePermissions(table, roleName, _action);
|
||||||
|
|
||||||
|
if (currPermissions) {
|
||||||
|
// existing permission is there
|
||||||
|
const deleteQuery = getDropPermissionQuery(
|
||||||
|
_action,
|
||||||
|
tableDef,
|
||||||
|
roleName
|
||||||
|
);
|
||||||
|
// since the actions must be revertible
|
||||||
|
const createQuery = getCreatePermissionQuery(
|
||||||
|
_action,
|
||||||
|
tableDef,
|
||||||
|
roleName,
|
||||||
|
currPermissions
|
||||||
|
);
|
||||||
|
|
||||||
|
permissionsUpQueries.push(deleteQuery);
|
||||||
|
permissionsDownQueries.push(createQuery);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply migration
|
||||||
|
const migrationName = `delete_role_${roleName}`;
|
||||||
|
|
||||||
|
const requestMsg = 'Deleting role';
|
||||||
|
const successMsg = 'Role Deleted';
|
||||||
|
const errorMsg = 'Role deletion failed';
|
||||||
|
|
||||||
|
const customOnSuccess = () => {
|
||||||
|
// fetch all roles
|
||||||
|
dispatch(fetchRoleList());
|
||||||
|
};
|
||||||
|
const customOnError = () => {};
|
||||||
|
|
||||||
|
makeMigrationCall(
|
||||||
|
dispatch,
|
||||||
|
getState,
|
||||||
|
permissionsUpQueries,
|
||||||
|
permissionsDownQueries,
|
||||||
|
migrationName,
|
||||||
|
customOnSuccess,
|
||||||
|
customOnError,
|
||||||
|
requestMsg,
|
||||||
|
successMsg,
|
||||||
|
errorMsg
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const permChangePermissions = changeType => {
|
const permChangePermissions = changeType => {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const allSchemas = getState().tables.allSchemas;
|
const allSchemas = getState().tables.allSchemas;
|
||||||
@ -790,4 +857,5 @@ export {
|
|||||||
permDelApplySamePerm,
|
permDelApplySamePerm,
|
||||||
applySamePermissionsBulk,
|
applySamePermissionsBulk,
|
||||||
copyRolePermissions,
|
copyRolePermissions,
|
||||||
|
deleteRoleGlobally,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user