Merge pull request #5944 from influxdata/5942/influxdb_admin_filter_redux
feat(ui/admin): remember state of influxdb users/roles filterspull/5949/head
commit
cbbd0ea829
|
@ -289,10 +289,14 @@ describe('InfluxDB', () => {
|
||||||
cy.get('th').contains('Users').should('exist')
|
cy.get('th').contains('Users').should('exist')
|
||||||
})
|
})
|
||||||
|
|
||||||
cy.getByTestID('hide-users--toggle').click()
|
cy.getByTestID('show-users--toggle').click()
|
||||||
cy.getByTestID('admin-table--head').within(() => {
|
cy.getByTestID('admin-table--head').within(() => {
|
||||||
cy.get('th').contains('Users').should('not.exist')
|
cy.get('th').contains('Users').should('not.exist')
|
||||||
})
|
})
|
||||||
|
cy.getByTestID('show-users--toggle').click()
|
||||||
|
cy.getByTestID('admin-table--head').within(() => {
|
||||||
|
cy.get('th').contains('Users').should('exist')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -473,3 +473,18 @@ export const updateUserPasswordAsync = (user, password) => async dispatch => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const changeSelectedDBs = (selectedDBs /* : string[] */) => ({
|
||||||
|
type: 'INFLUXDB_CHANGE_SELECTED_DBS',
|
||||||
|
payload: {
|
||||||
|
selectedDBs,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const changeShowUsers = () => ({
|
||||||
|
type: 'INFLUXDB_CHANGE_SHOW_USERS',
|
||||||
|
})
|
||||||
|
|
||||||
|
export const changeShowRoles = () => ({
|
||||||
|
type: 'INFLUXDB_CHANGE_SHOW_ROLES',
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {connect, ResolveThunks} from 'react-redux'
|
||||||
|
import {changeSelectedDBs} from 'src/admin/actions/influxdb'
|
||||||
|
import {MultiSelectDropdown} from 'src/reusable_ui'
|
||||||
|
import {Database} from 'src/types/influxAdmin'
|
||||||
|
|
||||||
|
interface ConnectedProps {
|
||||||
|
databases: Database[]
|
||||||
|
selectedDBs: string[]
|
||||||
|
}
|
||||||
|
const mapStateToProps = ({adminInfluxDB: {databases, selectedDBs}}) => ({
|
||||||
|
databases,
|
||||||
|
selectedDBs,
|
||||||
|
})
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
setSelectedDBs: changeSelectedDBs,
|
||||||
|
}
|
||||||
|
type ReduxDispatchProps = ResolveThunks<typeof mapDispatchToProps>
|
||||||
|
type Props = ConnectedProps & ReduxDispatchProps
|
||||||
|
const MultiDBSelector = ({databases, selectedDBs, setSelectedDBs}: Props) => {
|
||||||
|
return (
|
||||||
|
<div className="db-selector">
|
||||||
|
<MultiSelectDropdown
|
||||||
|
onChange={setSelectedDBs}
|
||||||
|
selectedIDs={selectedDBs}
|
||||||
|
emptyText="<no database>"
|
||||||
|
>
|
||||||
|
{databases.reduce(
|
||||||
|
(acc, db) => {
|
||||||
|
acc.push(
|
||||||
|
<MultiSelectDropdown.Item
|
||||||
|
key={db.name}
|
||||||
|
id={db.name}
|
||||||
|
value={{id: db.name}}
|
||||||
|
>
|
||||||
|
{db.name}
|
||||||
|
</MultiSelectDropdown.Item>
|
||||||
|
)
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
[
|
||||||
|
<MultiSelectDropdown.Item id="*" key="*" value={{id: '*'}}>
|
||||||
|
All Databases
|
||||||
|
</MultiSelectDropdown.Item>,
|
||||||
|
<MultiSelectDropdown.Divider id="" key="" />,
|
||||||
|
]
|
||||||
|
)}
|
||||||
|
</MultiSelectDropdown>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(MultiDBSelector)
|
|
@ -142,7 +142,7 @@ const RolePage = ({
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[roleDBPermissions, changedPermissions, setChangedPermissions]
|
[roleDBPermissions, changedPermissions]
|
||||||
)
|
)
|
||||||
const permissionsChanged = !!Object.keys(changedPermissions).length
|
const permissionsChanged = !!Object.keys(changedPermissions).length
|
||||||
const changePermissions = useMemo(
|
const changePermissions = useMemo(
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {Source, NotificationAction} from 'src/types'
|
||||||
import {UserRole, User, Database} from 'src/types/influxAdmin'
|
import {UserRole, User, Database} from 'src/types/influxAdmin'
|
||||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||||
import {
|
import {
|
||||||
|
changeShowUsers,
|
||||||
createRoleAsync,
|
createRoleAsync,
|
||||||
filterRoles as filterRolesAction,
|
filterRoles as filterRolesAction,
|
||||||
} from 'src/admin/actions/influxdb'
|
} from 'src/admin/actions/influxdb'
|
||||||
|
@ -21,14 +22,14 @@ import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||||
import NoEntities from 'src/admin/components/influxdb/NoEntities'
|
import NoEntities from 'src/admin/components/influxdb/NoEntities'
|
||||||
import RoleRow from 'src/admin/components/RoleRow'
|
import RoleRow from 'src/admin/components/RoleRow'
|
||||||
import {useCallback} from 'react'
|
import {useCallback} from 'react'
|
||||||
import allOrParticularSelection from '../../util/allOrParticularSelection'
|
|
||||||
import {computeEntitiesDBPermissions} from '../../util/computeEffectiveDBPermissions'
|
import {computeEntitiesDBPermissions} from '../../util/computeEffectiveDBPermissions'
|
||||||
import useDebounce from 'src/utils/useDebounce'
|
import useDebounce from 'src/utils/useDebounce'
|
||||||
import useChangeEffect from 'src/utils/useChangeEffect'
|
import useChangeEffect from 'src/utils/useChangeEffect'
|
||||||
import {ComponentSize, MultiSelectDropdown, SlideToggle} from 'src/reusable_ui'
|
import {ComponentSize, SlideToggle} from 'src/reusable_ui'
|
||||||
import CreateRoleDialog, {
|
import CreateRoleDialog, {
|
||||||
validateRoleName,
|
validateRoleName,
|
||||||
} from 'src/admin/components/influxdb/CreateRoleDialog'
|
} from 'src/admin/components/influxdb/CreateRoleDialog'
|
||||||
|
import MultiDBSelector from 'src/admin/components/influxdb/MultiDBSelector'
|
||||||
|
|
||||||
const validateRole = (
|
const validateRole = (
|
||||||
role: Pick<UserRole, 'name'>,
|
role: Pick<UserRole, 'name'>,
|
||||||
|
@ -41,16 +42,22 @@ const validateRole = (
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = ({adminInfluxDB: {databases, users, roles}}) => ({
|
const mapStateToProps = ({
|
||||||
|
adminInfluxDB: {databases, users, roles, selectedDBs, showUsers, rolesFilter},
|
||||||
|
}) => ({
|
||||||
databases,
|
databases,
|
||||||
users,
|
users,
|
||||||
roles,
|
roles,
|
||||||
|
selectedDBs,
|
||||||
|
showUsers,
|
||||||
|
rolesFilter,
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
filterRoles: filterRolesAction,
|
filterRoles: filterRolesAction,
|
||||||
createRole: createRoleAsync,
|
createRole: createRoleAsync,
|
||||||
notify: notifyAction,
|
notify: notifyAction,
|
||||||
|
toggleShowUsers: changeShowUsers,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OwnProps {
|
interface OwnProps {
|
||||||
|
@ -60,6 +67,9 @@ interface ConnectedProps {
|
||||||
databases: Database[]
|
databases: Database[]
|
||||||
users: User[]
|
users: User[]
|
||||||
roles: UserRole[]
|
roles: UserRole[]
|
||||||
|
selectedDBs: string[]
|
||||||
|
showUsers: boolean
|
||||||
|
rolesFilter: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReduxDispatchProps = ResolveThunks<typeof mapDispatchToProps>
|
type ReduxDispatchProps = ResolveThunks<typeof mapDispatchToProps>
|
||||||
|
@ -71,30 +81,26 @@ const RolesPage = ({
|
||||||
users,
|
users,
|
||||||
roles,
|
roles,
|
||||||
databases,
|
databases,
|
||||||
|
selectedDBs,
|
||||||
|
showUsers,
|
||||||
|
rolesFilter,
|
||||||
router,
|
router,
|
||||||
filterRoles,
|
filterRoles,
|
||||||
createRole,
|
createRole,
|
||||||
|
toggleShowUsers,
|
||||||
notify,
|
notify,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const rolesPage = useMemo(
|
const rolesPage = useMemo(
|
||||||
() => `/sources/${source.id}/admin-influxdb/roles`,
|
() => `/sources/${source.id}/admin-influxdb/roles`,
|
||||||
[source]
|
[source]
|
||||||
)
|
)
|
||||||
// filter databases
|
// database columns
|
||||||
const [selectedDBs, setSelectedDBs] = useState<string[]>(['*'])
|
|
||||||
const visibleDBNames = useMemo<string[]>(() => {
|
const visibleDBNames = useMemo<string[]>(() => {
|
||||||
if (selectedDBs.includes('*')) {
|
if (selectedDBs.includes('*')) {
|
||||||
return databases.map(db => db.name)
|
return databases.map(db => db.name)
|
||||||
}
|
}
|
||||||
return selectedDBs
|
return selectedDBs
|
||||||
}, [databases, selectedDBs])
|
}, [databases, selectedDBs])
|
||||||
const changeSelectedDBs = useCallback(
|
|
||||||
(newDBs: string[]) =>
|
|
||||||
setSelectedDBs((oldDBs: string[]) => {
|
|
||||||
return allOrParticularSelection(oldDBs, newDBs)
|
|
||||||
}),
|
|
||||||
[setSelectedDBs]
|
|
||||||
)
|
|
||||||
|
|
||||||
// effective permissions
|
// effective permissions
|
||||||
const visibleRoles = useMemo(() => roles.filter(x => !x.hidden), [roles])
|
const visibleRoles = useMemo(() => roles.filter(x => !x.hidden), [roles])
|
||||||
|
@ -103,23 +109,14 @@ const RolesPage = ({
|
||||||
[visibleDBNames, visibleRoles]
|
[visibleDBNames, visibleRoles]
|
||||||
)
|
)
|
||||||
|
|
||||||
// filter users
|
// filter roles
|
||||||
const [filterText, setFilterText] = useState('')
|
const [filterText, setFilterText] = useState(rolesFilter)
|
||||||
const changeFilterText = useCallback(e => setFilterText(e.target.value), [
|
const changeFilterText = useCallback(e => setFilterText(e.target.value), [])
|
||||||
setFilterText,
|
|
||||||
])
|
|
||||||
const debouncedFilterText = useDebounce(filterText, 200)
|
const debouncedFilterText = useDebounce(filterText, 200)
|
||||||
useChangeEffect(() => {
|
useChangeEffect(() => {
|
||||||
filterRoles(debouncedFilterText)
|
filterRoles(debouncedFilterText)
|
||||||
}, [debouncedFilterText])
|
}, [debouncedFilterText])
|
||||||
|
|
||||||
// hide users
|
|
||||||
const [showUsers, setShowUsers] = useState(true)
|
|
||||||
const changeHideUsers = useCallback(() => setShowUsers(!showUsers), [
|
|
||||||
showUsers,
|
|
||||||
setShowUsers,
|
|
||||||
])
|
|
||||||
|
|
||||||
const [createVisible, setCreateVisible] = useState(false)
|
const [createVisible, setCreateVisible] = useState(false)
|
||||||
const createNew = useCallback(
|
const createNew = useCallback(
|
||||||
async (role: {name: string}) => {
|
async (role: {name: string}) => {
|
||||||
|
@ -159,40 +156,13 @@ const RolesPage = ({
|
||||||
/>
|
/>
|
||||||
<span className="icon search" />
|
<span className="icon search" />
|
||||||
</div>
|
</div>
|
||||||
<div className="db-selector">
|
<MultiDBSelector />
|
||||||
<MultiSelectDropdown
|
|
||||||
onChange={changeSelectedDBs}
|
|
||||||
selectedIDs={selectedDBs}
|
|
||||||
emptyText="<no database>"
|
|
||||||
>
|
|
||||||
{databases.reduce(
|
|
||||||
(acc, db) => {
|
|
||||||
acc.push(
|
|
||||||
<MultiSelectDropdown.Item
|
|
||||||
key={db.name}
|
|
||||||
id={db.name}
|
|
||||||
value={{id: db.name}}
|
|
||||||
>
|
|
||||||
{db.name}
|
|
||||||
</MultiSelectDropdown.Item>
|
|
||||||
)
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
[
|
|
||||||
<MultiSelectDropdown.Item id="*" key="*" value={{id: '*'}}>
|
|
||||||
All Databases
|
|
||||||
</MultiSelectDropdown.Item>,
|
|
||||||
<MultiSelectDropdown.Divider id="" key="" />,
|
|
||||||
]
|
|
||||||
)}
|
|
||||||
</MultiSelectDropdown>
|
|
||||||
</div>
|
|
||||||
<div className="hide-roles-toggle">
|
<div className="hide-roles-toggle">
|
||||||
<SlideToggle
|
<SlideToggle
|
||||||
active={showUsers}
|
active={showUsers}
|
||||||
onChange={changeHideUsers}
|
onChange={toggleShowUsers}
|
||||||
size={ComponentSize.ExtraSmall}
|
size={ComponentSize.ExtraSmall}
|
||||||
entity="users"
|
dataTest="show-users--toggle"
|
||||||
/>
|
/>
|
||||||
Show Users
|
Show Users
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -173,7 +173,7 @@ const UserPage = ({
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[userDBPermissions, changedPermissions, setChangedPermissions]
|
[userDBPermissions, changedPermissions]
|
||||||
)
|
)
|
||||||
const permissionsChanged = !!Object.keys(changedPermissions).length
|
const permissionsChanged = !!Object.keys(changedPermissions).length
|
||||||
const changePermissions = useMemo(
|
const changePermissions = useMemo(
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {Source, NotificationAction} from 'src/types'
|
||||||
import {UserRole, User, Database} from 'src/types/influxAdmin'
|
import {UserRole, User, Database} from 'src/types/influxAdmin'
|
||||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||||
import {
|
import {
|
||||||
|
changeShowRoles,
|
||||||
createUserAsync,
|
createUserAsync,
|
||||||
filterUsers as filterUsersAction,
|
filterUsers as filterUsersAction,
|
||||||
} from 'src/admin/actions/influxdb'
|
} from 'src/admin/actions/influxdb'
|
||||||
|
@ -22,15 +23,14 @@ import NoEntities from 'src/admin/components/influxdb/NoEntities'
|
||||||
import UserRow from 'src/admin/components/UserRow'
|
import UserRow from 'src/admin/components/UserRow'
|
||||||
import useDebounce from 'src/utils/useDebounce'
|
import useDebounce from 'src/utils/useDebounce'
|
||||||
import useChangeEffect from 'src/utils/useChangeEffect'
|
import useChangeEffect from 'src/utils/useChangeEffect'
|
||||||
import MultiSelectDropdown from 'src/reusable_ui/components/dropdowns/MultiSelectDropdown'
|
|
||||||
import {ComponentSize, SlideToggle} from 'src/reusable_ui'
|
import {ComponentSize, SlideToggle} from 'src/reusable_ui'
|
||||||
import {computeEffectiveUserDBPermissions} from '../../util/computeEffectiveDBPermissions'
|
import {computeEffectiveUserDBPermissions} from '../../util/computeEffectiveDBPermissions'
|
||||||
import allOrParticularSelection from '../../util/allOrParticularSelection'
|
|
||||||
import CreateUserDialog, {
|
import CreateUserDialog, {
|
||||||
validatePassword,
|
validatePassword,
|
||||||
validateUserName,
|
validateUserName,
|
||||||
} from '../../components/influxdb/CreateUserDialog'
|
} from '../../components/influxdb/CreateUserDialog'
|
||||||
import {withRouter, WithRouterProps} from 'react-router'
|
import {withRouter, WithRouterProps} from 'react-router'
|
||||||
|
import MultiDBSelector from 'src/admin/components/influxdb/MultiDBSelector'
|
||||||
|
|
||||||
const validateUser = (
|
const validateUser = (
|
||||||
user: Pick<User, 'name' | 'password'>,
|
user: Pick<User, 'name' | 'password'>,
|
||||||
|
@ -47,15 +47,21 @@ const validateUser = (
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = ({adminInfluxDB: {databases, users, roles}}) => ({
|
const mapStateToProps = ({
|
||||||
|
adminInfluxDB: {databases, users, roles, selectedDBs, showRoles, usersFilter},
|
||||||
|
}) => ({
|
||||||
databases,
|
databases,
|
||||||
users,
|
users,
|
||||||
roles,
|
roles,
|
||||||
|
selectedDBs,
|
||||||
|
showRoles,
|
||||||
|
usersFilter,
|
||||||
})
|
})
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
filterUsers: filterUsersAction,
|
filterUsers: filterUsersAction,
|
||||||
createUser: createUserAsync,
|
createUser: createUserAsync,
|
||||||
|
toggleShowRoles: changeShowRoles,
|
||||||
notify: notifyAction,
|
notify: notifyAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +72,9 @@ interface ConnectedProps {
|
||||||
databases: Database[]
|
databases: Database[]
|
||||||
users: User[]
|
users: User[]
|
||||||
roles: UserRole[]
|
roles: UserRole[]
|
||||||
|
selectedDBs: string[]
|
||||||
|
showRoles: boolean
|
||||||
|
usersFilter: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReduxDispatchProps = ResolveThunks<typeof mapDispatchToProps>
|
type ReduxDispatchProps = ResolveThunks<typeof mapDispatchToProps>
|
||||||
|
@ -77,9 +86,13 @@ const UsersPage = ({
|
||||||
databases,
|
databases,
|
||||||
users,
|
users,
|
||||||
roles,
|
roles,
|
||||||
|
selectedDBs,
|
||||||
|
showRoles,
|
||||||
|
usersFilter,
|
||||||
notify,
|
notify,
|
||||||
createUser,
|
createUser,
|
||||||
filterUsers,
|
filterUsers,
|
||||||
|
toggleShowRoles,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [isEnterprise, usersPage] = useMemo(
|
const [isEnterprise, usersPage] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
|
@ -88,21 +101,13 @@ const UsersPage = ({
|
||||||
],
|
],
|
||||||
[source]
|
[source]
|
||||||
)
|
)
|
||||||
// filter databases
|
// database columns
|
||||||
const [selectedDBs, setSelectedDBs] = useState<string[]>(['*'])
|
|
||||||
const visibleDBNames = useMemo<string[]>(() => {
|
const visibleDBNames = useMemo<string[]>(() => {
|
||||||
if (selectedDBs.includes('*')) {
|
if (selectedDBs.includes('*')) {
|
||||||
return databases.map(db => db.name)
|
return databases.map(db => db.name)
|
||||||
}
|
}
|
||||||
return selectedDBs
|
return selectedDBs
|
||||||
}, [databases, selectedDBs])
|
}, [databases, selectedDBs])
|
||||||
const changeSelectedDBs = useCallback(
|
|
||||||
(newDBs: string[]) =>
|
|
||||||
setSelectedDBs((oldDBs: string[]) => {
|
|
||||||
return allOrParticularSelection(oldDBs, newDBs)
|
|
||||||
}),
|
|
||||||
[setSelectedDBs]
|
|
||||||
)
|
|
||||||
|
|
||||||
// effective permissions
|
// effective permissions
|
||||||
const visibleUsers = useMemo(() => users.filter(x => !x.hidden), [users])
|
const visibleUsers = useMemo(() => users.filter(x => !x.hidden), [users])
|
||||||
|
@ -113,22 +118,13 @@ const UsersPage = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
// filter users
|
// filter users
|
||||||
const [filterText, setFilterText] = useState('')
|
const [filterText, setFilterText] = useState(usersFilter)
|
||||||
const changeFilterText = useCallback(e => setFilterText(e.target.value), [
|
const changeFilterText = useCallback(e => setFilterText(e.target.value), [])
|
||||||
setFilterText,
|
|
||||||
])
|
|
||||||
const debouncedFilterText = useDebounce(filterText, 200)
|
const debouncedFilterText = useDebounce(filterText, 200)
|
||||||
useChangeEffect(() => {
|
useChangeEffect(() => {
|
||||||
filterUsers(debouncedFilterText)
|
filterUsers(debouncedFilterText)
|
||||||
}, [debouncedFilterText])
|
}, [debouncedFilterText])
|
||||||
|
|
||||||
// hide role
|
|
||||||
const [showRoles, setShowRoles] = useState(true)
|
|
||||||
const changeHideRoles = useCallback(() => setShowRoles(!showRoles), [
|
|
||||||
showRoles,
|
|
||||||
setShowRoles,
|
|
||||||
])
|
|
||||||
|
|
||||||
const [createVisible, setCreateVisible] = useState(false)
|
const [createVisible, setCreateVisible] = useState(false)
|
||||||
const createNew = useCallback(
|
const createNew = useCallback(
|
||||||
async (user: {name: string; password: string}) => {
|
async (user: {name: string; password: string}) => {
|
||||||
|
@ -169,39 +165,12 @@ const UsersPage = ({
|
||||||
/>
|
/>
|
||||||
<span className="icon search" />
|
<span className="icon search" />
|
||||||
</div>
|
</div>
|
||||||
<div className="db-selector" data-test="db-selector">
|
<MultiDBSelector />
|
||||||
<MultiSelectDropdown
|
|
||||||
onChange={changeSelectedDBs}
|
|
||||||
selectedIDs={selectedDBs}
|
|
||||||
emptyText="<no database>"
|
|
||||||
>
|
|
||||||
{databases.reduce(
|
|
||||||
(acc, db) => {
|
|
||||||
acc.push(
|
|
||||||
<MultiSelectDropdown.Item
|
|
||||||
key={db.name}
|
|
||||||
id={db.name}
|
|
||||||
value={{id: db.name}}
|
|
||||||
>
|
|
||||||
{db.name}
|
|
||||||
</MultiSelectDropdown.Item>
|
|
||||||
)
|
|
||||||
return acc
|
|
||||||
},
|
|
||||||
[
|
|
||||||
<MultiSelectDropdown.Item id="*" key="*" value={{id: '*'}}>
|
|
||||||
All Databases
|
|
||||||
</MultiSelectDropdown.Item>,
|
|
||||||
<MultiSelectDropdown.Divider id="" key="" />,
|
|
||||||
]
|
|
||||||
)}
|
|
||||||
</MultiSelectDropdown>
|
|
||||||
</div>
|
|
||||||
{isEnterprise && (
|
{isEnterprise && (
|
||||||
<div className="hide-roles-toggle">
|
<div className="hide-roles-toggle">
|
||||||
<SlideToggle
|
<SlideToggle
|
||||||
active={showRoles}
|
active={showRoles}
|
||||||
onChange={changeHideRoles}
|
onChange={toggleShowRoles}
|
||||||
size={ComponentSize.ExtraSmall}
|
size={ComponentSize.ExtraSmall}
|
||||||
/>
|
/>
|
||||||
Show Roles
|
Show Roles
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
changeNamedCollection,
|
changeNamedCollection,
|
||||||
computeNamedChanges,
|
computeNamedChanges,
|
||||||
} from '../util/changeNamedCollection'
|
} from '../util/changeNamedCollection'
|
||||||
|
import allOrParticularSelection from '../util/allOrParticularSelection'
|
||||||
|
|
||||||
const querySorters = {
|
const querySorters = {
|
||||||
'+time'(queries) {
|
'+time'(queries) {
|
||||||
|
@ -37,8 +38,7 @@ const identity = x => x
|
||||||
function sortQueries(queries, queriesSort) {
|
function sortQueries(queries, queriesSort) {
|
||||||
return (querySorters[queriesSort] || identity)(queries)
|
return (querySorters[queriesSort] || identity)(queries)
|
||||||
}
|
}
|
||||||
|
export const initialState = {
|
||||||
const initialState = {
|
|
||||||
users: [],
|
users: [],
|
||||||
roles: [],
|
roles: [],
|
||||||
permissions: [],
|
permissions: [],
|
||||||
|
@ -46,16 +46,21 @@ const initialState = {
|
||||||
queriesSort: '-time',
|
queriesSort: '-time',
|
||||||
queryIDToKill: null,
|
queryIDToKill: null,
|
||||||
databases: [],
|
databases: [],
|
||||||
|
selectedDBs: ['*'],
|
||||||
|
showUsers: true,
|
||||||
|
showRoles: true,
|
||||||
|
usersFilter: '',
|
||||||
|
rolesFilter: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminInfluxDB = (state = initialState, action) => {
|
const adminInfluxDB = (state = initialState, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'INFLUXDB_LOAD_USERS': {
|
case 'INFLUXDB_LOAD_USERS': {
|
||||||
return {...state, ...action.payload}
|
return {...state, ...action.payload, usersFilter: ''}
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'INFLUXDB_LOAD_ROLES': {
|
case 'INFLUXDB_LOAD_ROLES': {
|
||||||
return {...state, ...action.payload}
|
return {...state, ...action.payload, rolesFilter: ''}
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'INFLUXDB_LOAD_PERMISSIONS': {
|
case 'INFLUXDB_LOAD_PERMISSIONS': {
|
||||||
|
@ -63,7 +68,9 @@ const adminInfluxDB = (state = initialState, action) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'INFLUXDB_LOAD_DATABASES': {
|
case 'INFLUXDB_LOAD_DATABASES': {
|
||||||
return {...state, ...action.payload}
|
const databases = action.payload.databases
|
||||||
|
const selectedDBs = initialState.selectedDBs
|
||||||
|
return {...state, databases, selectedDBs}
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'INFLUXDB_ADD_DATABASE': {
|
case 'INFLUXDB_ADD_DATABASE': {
|
||||||
|
@ -333,7 +340,7 @@ const adminInfluxDB = (state = initialState, action) => {
|
||||||
return u
|
return u
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
return {...state, ...newState}
|
return {...state, ...newState, usersFilter: text}
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'INFLUXDB_FILTER_ROLES': {
|
case 'INFLUXDB_FILTER_ROLES': {
|
||||||
|
@ -344,7 +351,7 @@ const adminInfluxDB = (state = initialState, action) => {
|
||||||
return r
|
return r
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
return {...state, ...newState}
|
return {...state, ...newState, rolesFilter: text}
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'INFLUXDB_KILL_QUERY': {
|
case 'INFLUXDB_KILL_QUERY': {
|
||||||
|
@ -359,6 +366,18 @@ const adminInfluxDB = (state = initialState, action) => {
|
||||||
case 'INFLUXDB_SET_QUERY_TO_KILL': {
|
case 'INFLUXDB_SET_QUERY_TO_KILL': {
|
||||||
return {...state, ...action.payload}
|
return {...state, ...action.payload}
|
||||||
}
|
}
|
||||||
|
case 'INFLUXDB_CHANGE_SELECTED_DBS': {
|
||||||
|
const newDBs = action.payload.selectedDBs
|
||||||
|
const oldDBs = state.selectedDBs || ['*']
|
||||||
|
const selectedDBs = allOrParticularSelection(oldDBs, newDBs)
|
||||||
|
return {...state, selectedDBs}
|
||||||
|
}
|
||||||
|
case 'INFLUXDB_CHANGE_SHOW_USERS': {
|
||||||
|
return {...state, showUsers: !state.showUsers}
|
||||||
|
}
|
||||||
|
case 'INFLUXDB_CHANGE_SHOW_ROLES': {
|
||||||
|
return {...state, showRoles: !state.showRoles}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {defaultTableData} from 'src/logs/constants'
|
||||||
import {VERSION, GIT_SHA} from 'src/shared/constants'
|
import {VERSION, GIT_SHA} from 'src/shared/constants'
|
||||||
|
|
||||||
import {LocalStorage} from 'src/types/localStorage'
|
import {LocalStorage} from 'src/types/localStorage'
|
||||||
|
import {initialState as adminInfluxDBInitialState} from './admin/reducers/influxdb'
|
||||||
|
|
||||||
export const loadLocalStorage = (
|
export const loadLocalStorage = (
|
||||||
errorsQueue: any[]
|
errorsQueue: any[]
|
||||||
|
@ -39,7 +40,7 @@ export const loadLocalStorage = (
|
||||||
|
|
||||||
delete state.VERSION
|
delete state.VERSION
|
||||||
delete state.GIT_SHA
|
delete state.GIT_SHA
|
||||||
|
state.adminInfluxDB = {...adminInfluxDBInitialState, ...state.adminInfluxDB}
|
||||||
return state
|
return state
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(notifyLoadLocalSettingsFailed(error).message)
|
console.error(notifyLoadLocalSettingsFailed(error).message)
|
||||||
|
@ -55,6 +56,7 @@ export const saveToLocalStorage = ({
|
||||||
dashTimeV1: {ranges, refreshes},
|
dashTimeV1: {ranges, refreshes},
|
||||||
logs,
|
logs,
|
||||||
script,
|
script,
|
||||||
|
adminInfluxDB: {showUsers, showRoles},
|
||||||
}: LocalStorage): void => {
|
}: LocalStorage): void => {
|
||||||
try {
|
try {
|
||||||
const dashTimeV1 = {
|
const dashTimeV1 = {
|
||||||
|
@ -104,6 +106,7 @@ export const saveToLocalStorage = ({
|
||||||
},
|
},
|
||||||
tableTime: minimalLogs.tableTime || {},
|
tableTime: minimalLogs.tableTime || {},
|
||||||
},
|
},
|
||||||
|
adminInfluxDB: {showRoles, showUsers},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ interface Props {
|
||||||
color?: ComponentColor
|
color?: ComponentColor
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
tooltipText?: string
|
tooltipText?: string
|
||||||
entity?: string
|
dataTest?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
|
@ -27,14 +27,14 @@ class SlideToggle extends Component<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {tooltipText} = this.props
|
const {tooltipText, dataTest} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={this.className}
|
className={this.className}
|
||||||
onClick={this.handleClick}
|
onClick={this.handleClick}
|
||||||
title={tooltipText}
|
title={tooltipText}
|
||||||
data-test={this.dataTest}
|
data-test={dataTest}
|
||||||
>
|
>
|
||||||
<div className="slide-toggle--knob" />
|
<div className="slide-toggle--knob" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,12 +59,6 @@ class SlideToggle extends Component<Props> {
|
||||||
{active, disabled}
|
{active, disabled}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private get dataTest(): string {
|
|
||||||
const {active, entity} = this.props
|
|
||||||
|
|
||||||
return active ? `hide-${entity}--toggle` : `show-${entity}--toggle`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SlideToggle
|
export default SlideToggle
|
||||||
|
|
|
@ -10,6 +10,10 @@ export interface LocalStorage {
|
||||||
logs: LogsState
|
logs: LogsState
|
||||||
telegrafSystemInterval: string
|
telegrafSystemInterval: string
|
||||||
hostPageDisabled: boolean
|
hostPageDisabled: boolean
|
||||||
|
adminInfluxDB: {
|
||||||
|
showUsers: boolean
|
||||||
|
showRoles: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VERSION = string
|
export type VERSION = string
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
syncRole,
|
syncRole,
|
||||||
editDatabase,
|
editDatabase,
|
||||||
editRetentionPolicyRequested,
|
editRetentionPolicyRequested,
|
||||||
|
loadUsers,
|
||||||
loadRoles,
|
loadRoles,
|
||||||
loadPermissions,
|
loadPermissions,
|
||||||
deleteRole,
|
deleteRole,
|
||||||
|
@ -19,6 +20,10 @@ import {
|
||||||
removeDatabaseDeleteCode,
|
removeDatabaseDeleteCode,
|
||||||
loadQueries,
|
loadQueries,
|
||||||
setQueriesSort,
|
setQueriesSort,
|
||||||
|
loadDatabases,
|
||||||
|
changeSelectedDBs,
|
||||||
|
changeShowUsers,
|
||||||
|
changeShowRoles,
|
||||||
} from 'src/admin/actions/influxdb'
|
} from 'src/admin/actions/influxdb'
|
||||||
|
|
||||||
import {NEW_DEFAULT_DATABASE, NEW_EMPTY_RP} from 'src/admin/constants'
|
import {NEW_DEFAULT_DATABASE, NEW_EMPTY_RP} from 'src/admin/constants'
|
||||||
|
@ -137,6 +142,17 @@ describe('Admin.InfluxDB.Reducers', () => {
|
||||||
state = {databases: [db1, db2]}
|
state = {databases: [db1, db2]}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('can load databases', () => {
|
||||||
|
const {databases, selectedDBs} = reducer(
|
||||||
|
undefined,
|
||||||
|
loadDatabases([{name: 'db1'}])
|
||||||
|
)
|
||||||
|
expect({databases, selectedDBs}).toEqual({
|
||||||
|
databases: [{name: 'db1'}],
|
||||||
|
selectedDBs: ['*'],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('can add a database', () => {
|
it('can add a database', () => {
|
||||||
const actual = reducer(state, addDatabase())
|
const actual = reducer(state, addDatabase())
|
||||||
const expected = [{...NEW_DEFAULT_DATABASE, isEditing: true}, db1, db2]
|
const expected = [{...NEW_DEFAULT_DATABASE, isEditing: true}, db1, db2]
|
||||||
|
@ -209,7 +225,15 @@ describe('Admin.InfluxDB.Reducers', () => {
|
||||||
expect(actual.databases).toEqual(expected)
|
expect(actual.databases).toEqual(expected)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
it('it can load users', () => {
|
||||||
|
const {users: d, usersFilter} = reducer(state, loadUsers({users}))
|
||||||
|
const expected = {
|
||||||
|
users,
|
||||||
|
usersFilter: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
expect({users: d, usersFilter}).toEqual(expected)
|
||||||
|
})
|
||||||
it('it can sync a stale user', () => {
|
it('it can sync a stale user', () => {
|
||||||
const staleUser = {...u1, roles: []}
|
const staleUser = {...u1, roles: []}
|
||||||
state = {users: [u2, staleUser], roles: []}
|
state = {users: [u2, staleUser], roles: []}
|
||||||
|
@ -315,13 +339,14 @@ describe('Admin.InfluxDB.Reducers', () => {
|
||||||
expect(actual.users).toEqual(expected.users)
|
expect(actual.users).toEqual(expected.users)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('it can load the roles', () => {
|
it('it can load roles', () => {
|
||||||
const actual = reducer(state, loadRoles({roles}))
|
const {roles: d, rolesFilter} = reducer(state, loadRoles({roles}))
|
||||||
const expected = {
|
const expected = {
|
||||||
roles,
|
roles,
|
||||||
|
rolesFilter: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(actual.roles).toEqual(expected.roles)
|
expect({roles: d, rolesFilter}).toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('it can delete a non-existing role', () => {
|
it('it can delete a non-existing role', () => {
|
||||||
|
@ -382,15 +407,16 @@ describe('Admin.InfluxDB.Reducers', () => {
|
||||||
|
|
||||||
const text = 'x'
|
const text = 'x'
|
||||||
|
|
||||||
const actual = reducer(state, filterRoles(text))
|
const {roles: d, rolesFilter} = reducer(state, filterRoles(text))
|
||||||
const expected = {
|
const expected = {
|
||||||
roles: [
|
roles: [
|
||||||
{...r1, hidden: false},
|
{...r1, hidden: false},
|
||||||
{...r2, hidden: true},
|
{...r2, hidden: true},
|
||||||
],
|
],
|
||||||
|
rolesFilter: text,
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(actual.roles).toEqual(expected.roles)
|
expect({roles: d, rolesFilter}).toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can filter users w/ "zero" text', () => {
|
it('can filter users w/ "zero" text', () => {
|
||||||
|
@ -400,15 +426,16 @@ describe('Admin.InfluxDB.Reducers', () => {
|
||||||
|
|
||||||
const text = 'zero'
|
const text = 'zero'
|
||||||
|
|
||||||
const actual = reducer(state, filterUsers(text))
|
const {users: d, usersFilter} = reducer(state, filterUsers(text))
|
||||||
const expected = {
|
const expected = {
|
||||||
users: [
|
users: [
|
||||||
{...u1, hidden: true},
|
{...u1, hidden: true},
|
||||||
{...u2, hidden: false},
|
{...u2, hidden: false},
|
||||||
],
|
],
|
||||||
|
usersFilter: text,
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(actual.users).toEqual(expected.users)
|
expect({users: d, usersFilter}).toEqual(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
|
@ -488,4 +515,56 @@ describe('Admin.InfluxDB.Reducers', () => {
|
||||||
expect(actual.queries[2].id).toEqual(1)
|
expect(actual.queries[2].id).toEqual(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe('filters', () => {
|
||||||
|
it('can change selected DBS', () => {
|
||||||
|
const testPairs = [
|
||||||
|
{
|
||||||
|
prev: undefined,
|
||||||
|
change: ['db1'],
|
||||||
|
next: ['db1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: [],
|
||||||
|
change: ['db1'],
|
||||||
|
next: ['db1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: ['db1'],
|
||||||
|
change: ['db1', '*'],
|
||||||
|
next: ['*'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: ['*'],
|
||||||
|
change: ['db1', '*'],
|
||||||
|
next: ['db1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prev: ['db1'],
|
||||||
|
change: [],
|
||||||
|
next: [],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
testPairs.forEach(({prev, change, next}) => {
|
||||||
|
const {selectedDBs} = reducer(
|
||||||
|
{selectedDBs: prev},
|
||||||
|
changeSelectedDBs(change)
|
||||||
|
)
|
||||||
|
expect(selectedDBs).toEqual(next)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('can change showUsers flag', () => {
|
||||||
|
const vals = [undefined, true, false]
|
||||||
|
vals.forEach(prev => {
|
||||||
|
const {showUsers} = reducer({showUsers: prev}, changeShowUsers())
|
||||||
|
expect(showUsers).toEqual(!prev)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('can change showRoles flag', () => {
|
||||||
|
const vals = [undefined, true, false]
|
||||||
|
vals.forEach(prev => {
|
||||||
|
const {showRoles} = reducer({showRoles: prev}, changeShowRoles())
|
||||||
|
expect(showRoles).toEqual(!prev)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue