mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +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)
|
||||
|
||||
- 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`
|
||||
|
||||
|
@ -532,6 +532,7 @@ const makeMigrationCall = (
|
||||
successMsg,
|
||||
errorMsg,
|
||||
shouldSkipSchemaReload,
|
||||
skipExecution = false,
|
||||
isRetry
|
||||
) => {
|
||||
const upQuery = {
|
||||
@ -548,6 +549,7 @@ const makeMigrationCall = (
|
||||
name: sanitize(migrationName),
|
||||
up: upQuery.args,
|
||||
down: downQuery.args,
|
||||
skip_execution: skipExecution,
|
||||
};
|
||||
|
||||
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 { updateSchemaInfo } from '../DataActions';
|
||||
import { copyRolePermissions, permOpenEdit } from '../TablePermissions/Actions';
|
||||
import {
|
||||
copyRolePermissions,
|
||||
permOpenEdit,
|
||||
deleteRoleGlobally,
|
||||
} from '../TablePermissions/Actions';
|
||||
|
||||
import {
|
||||
getAllRoles,
|
||||
@ -33,6 +37,9 @@ import {
|
||||
getPermissionRowAccessSummary,
|
||||
} from './utils';
|
||||
|
||||
import Header from './Header';
|
||||
import RolesHeader from './RolesHeader';
|
||||
|
||||
class PermissionsSummary extends Component {
|
||||
initState = {
|
||||
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) => {
|
||||
return () => {
|
||||
dispatch(
|
||||
@ -247,66 +208,46 @@ class PermissionsSummary extends Component {
|
||||
};
|
||||
};
|
||||
|
||||
const getRolesHeaders = (selectable = true, selectedFirst = false) => {
|
||||
const rolesHeaders = [];
|
||||
const copyOnClick = (e, role) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if (!allRoles.length) {
|
||||
rolesHeaders.push(getHeader('No roles', false));
|
||||
} else {
|
||||
allRoles.forEach(role => {
|
||||
const isCurrRole = currRole === role;
|
||||
this.setState({
|
||||
copyState: {
|
||||
...copyState,
|
||||
copyFromRole: role,
|
||||
copyFromTable: currTable ? getTableNameWithSchema(currTable) : 'all',
|
||||
copyFromAction: currRole ? 'all' : currAction,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const setRole = () => {
|
||||
this.setState({ currRole: isCurrRole ? null : role });
|
||||
window.scrollTo(0, 0);
|
||||
};
|
||||
const deleteOnClick = (e, role) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const getCopyBtn = () => {
|
||||
const copyOnClick = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const deleteConfirmed = getConfirmation(
|
||||
`This will delete all permissions for the role: "${role}" for all entities in the current Postgres schema`,
|
||||
true,
|
||||
role
|
||||
);
|
||||
|
||||
this.setState({
|
||||
copyState: {
|
||||
...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);
|
||||
}
|
||||
});
|
||||
if (deleteConfirmed) {
|
||||
dispatch(deleteRoleGlobally(role));
|
||||
}
|
||||
};
|
||||
|
||||
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) => {
|
||||
@ -366,7 +307,9 @@ class PermissionsSummary extends Component {
|
||||
|
||||
if (!currSchemaTrackedTables.length) {
|
||||
tablesRows.push(
|
||||
<tr key={'No tables'}>{getHeader('No tables', false)}</tr>
|
||||
<tr key={'No tables'}>
|
||||
<Header content="No tables" selectable={false} />
|
||||
</tr>
|
||||
);
|
||||
} else {
|
||||
currSchemaTrackedTables.forEach((table, i) => {
|
||||
@ -385,13 +328,15 @@ class PermissionsSummary extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
return getHeader(
|
||||
displayTableName(table),
|
||||
selectable,
|
||||
isCurrTable,
|
||||
setTable,
|
||||
null,
|
||||
tableName
|
||||
return (
|
||||
<Header
|
||||
content={displayTableName(table)}
|
||||
selectable={selectable}
|
||||
isSelected={isCurrTable}
|
||||
onClick={setTable}
|
||||
actionButtons={[]}
|
||||
key={tableName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -631,7 +576,7 @@ class PermissionsSummary extends Component {
|
||||
<thead>
|
||||
<tr>
|
||||
{getActionSelector()}
|
||||
{getRolesHeaders(false)}
|
||||
<RolesHeader selectable={false} {...defaultRolesHeaderProps} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -656,7 +601,11 @@ class PermissionsSummary extends Component {
|
||||
return (
|
||||
<tr>
|
||||
{getBackBtn('currRole')}
|
||||
{getRolesHeaders(true, true)}
|
||||
<RolesHeader
|
||||
selectable
|
||||
selectedFirst
|
||||
{...defaultRolesHeaderProps}
|
||||
/>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
@ -715,7 +664,7 @@ class PermissionsSummary extends Component {
|
||||
return (
|
||||
<tr>
|
||||
{getActionSelector()}
|
||||
{getRolesHeaders()}
|
||||
<RolesHeader {...defaultRolesHeaderProps} />
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
@import "../../../Common/Common";
|
||||
@import '../../../Common/Common';
|
||||
|
||||
//.rolesTableWrapper {
|
||||
//}
|
||||
@ -6,7 +6,8 @@
|
||||
.rolesTable {
|
||||
width: fit-content;
|
||||
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
@ -25,7 +26,7 @@
|
||||
|
||||
.selected,
|
||||
.selected th {
|
||||
background-color: #FFF3D5 !important;
|
||||
background-color: #fff3d5 !important;
|
||||
}
|
||||
|
||||
.permissionSymbolNA {
|
||||
@ -71,3 +72,8 @@
|
||||
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 KnowMoreLink from '../../../Common/KnowMoreLink/KnowMoreLink';
|
||||
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 {
|
||||
constructor(props) {
|
||||
@ -54,8 +155,44 @@ class Schema extends Component {
|
||||
this.props.dispatch(
|
||||
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() {
|
||||
const {
|
||||
schema,
|
||||
@ -70,14 +207,10 @@ class Schema extends Component {
|
||||
trackedFunctions,
|
||||
} = this.props;
|
||||
|
||||
const styles = require('../../../Common/Common.scss');
|
||||
|
||||
const handleSchemaChange = e => {
|
||||
dispatch(updateCurrentSchema(e.target.value));
|
||||
};
|
||||
|
||||
/***********/
|
||||
|
||||
const _getTrackableFunctions = () => {
|
||||
const trackedFuncNames = trackedFunctions.map(fn => getFunctionName(fn));
|
||||
|
||||
@ -104,8 +237,6 @@ class Schema extends Component {
|
||||
);
|
||||
};
|
||||
|
||||
/***********/
|
||||
|
||||
const allUntrackedTables = getUntrackedTables(
|
||||
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 (
|
||||
<div className={styles.add_mar_top}>
|
||||
<div className={styles.display_inline}>Current Postgres schema</div>
|
||||
<div className={styles.display_inline}>
|
||||
<select
|
||||
onChange={handleSchemaChange}
|
||||
className={
|
||||
styles.add_mar_left_mid +
|
||||
' ' +
|
||||
styles.width_auto +
|
||||
' form-control'
|
||||
}
|
||||
className={`${styles.add_mar_left_mid} ${styles.width_auto} form-control`}
|
||||
value={currentSchema}
|
||||
>
|
||||
{getSchemaOptions()}
|
||||
</select>
|
||||
</div>
|
||||
<div className={styles.display_inline + ' ' + styles.add_mar_left}>
|
||||
<div className={styles.display_inline}>{getDeleteSchemaBtn()}</div>
|
||||
<div className={`${styles.display_inline} ${styles.add_mar_left}`}>
|
||||
<div className={styles.display_inline}>
|
||||
<DeleteSchemaButton
|
||||
dispatch={dispatch}
|
||||
migrationMode={migrationMode}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
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>
|
||||
@ -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 (
|
||||
<div
|
||||
className={`container-fluid ${styles.padd_left_remove} ${styles.padd_top}`}
|
||||
@ -703,7 +717,6 @@ class Schema extends Component {
|
||||
{getUntrackedFunctionsSection()}
|
||||
{getNonTrackableFunctionsSection()}
|
||||
<hr />
|
||||
{getPermissionsSummaryLink()}
|
||||
</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 => {
|
||||
return (dispatch, getState) => {
|
||||
const allSchemas = getState().tables.allSchemas;
|
||||
@ -790,4 +857,5 @@ export {
|
||||
permDelApplySamePerm,
|
||||
applySamePermissionsBulk,
|
||||
copyRolePermissions,
|
||||
deleteRoleGlobally,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user