Refactor getMe & refresh me for SideNav upon change organizations
parent
e1e81bee5e
commit
9c8cac04a9
|
@ -4,7 +4,7 @@ import {
|
||||||
authExpired,
|
authExpired,
|
||||||
authRequested,
|
authRequested,
|
||||||
authReceived,
|
authReceived,
|
||||||
meRequested,
|
meGetRequested,
|
||||||
meReceivedNotUsingAuth,
|
meReceivedNotUsingAuth,
|
||||||
} from 'shared/actions/auth'
|
} from 'shared/actions/auth'
|
||||||
|
|
||||||
|
@ -51,8 +51,8 @@ describe('Shared.Reducers.authReducer', () => {
|
||||||
expect(reducedState.isAuthLoading).to.equal(false)
|
expect(reducedState.isAuthLoading).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle ME_REQUESTED', () => {
|
it('should handle ME_GET_REQUESTED', () => {
|
||||||
const reducedState = authReducer(initialState, meRequested())
|
const reducedState = authReducer(initialState, meGetRequested())
|
||||||
|
|
||||||
expect(reducedState.isMeLoading).to.equal(true)
|
expect(reducedState.isMeLoading).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,30 +3,37 @@ import {connect} from 'react-redux'
|
||||||
import {bindActionCreators} from 'redux'
|
import {bindActionCreators} from 'redux'
|
||||||
|
|
||||||
import * as adminChronografActionCreators from 'src/admin/actions/chronograf'
|
import * as adminChronografActionCreators from 'src/admin/actions/chronograf'
|
||||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
import {getMeAsync} from 'shared/actions/auth'
|
||||||
|
|
||||||
import OrganizationsTable from 'src/admin/components/chronograf/OrganizationsTable'
|
import OrganizationsTable from 'src/admin/components/chronograf/OrganizationsTable'
|
||||||
|
|
||||||
class OrganizationsPage extends Component {
|
class OrganizationsPage extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {links, actions: {loadOrganizationsAsync}} = this.props
|
const {links, actions: {loadOrganizationsAsync}} = this.props
|
||||||
|
|
||||||
loadOrganizationsAsync(links.organizations)
|
loadOrganizationsAsync(links.organizations)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCreateOrganization = organization => {
|
handleCreateOrganization = async organization => {
|
||||||
const {links, actions: {createOrganizationAsync}} = this.props
|
const {links, actions: {createOrganizationAsync}} = this.props
|
||||||
createOrganizationAsync(links.organizations, organization)
|
await createOrganizationAsync(links.organizations, organization)
|
||||||
|
this.refreshMe()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRenameOrganization = (organization, name) => {
|
handleRenameOrganization = async (organization, name) => {
|
||||||
const {actions: {updateOrganizationAsync}} = this.props
|
const {actions: {updateOrganizationAsync}} = this.props
|
||||||
updateOrganizationAsync(organization, {...organization, name})
|
await updateOrganizationAsync(organization, {...organization, name})
|
||||||
|
this.refreshMe()
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteOrganization = organization => {
|
handleDeleteOrganization = organization => {
|
||||||
const {actions: {deleteOrganizationAsync}} = this.props
|
const {actions: {deleteOrganizationAsync}} = this.props
|
||||||
deleteOrganizationAsync(organization)
|
deleteOrganizationAsync(organization)
|
||||||
|
this.refreshMe()
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshMe = () => {
|
||||||
|
const {getMe} = this.props
|
||||||
|
getMe({shouldResetMe: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTogglePublic = organization => {
|
handleTogglePublic = organization => {
|
||||||
|
@ -40,6 +47,8 @@ class OrganizationsPage extends Component {
|
||||||
handleChooseDefaultRole = (organization, defaultRole) => {
|
handleChooseDefaultRole = (organization, defaultRole) => {
|
||||||
const {actions: {updateOrganizationAsync}} = this.props
|
const {actions: {updateOrganizationAsync}} = this.props
|
||||||
updateOrganizationAsync(organization, {...organization, defaultRole})
|
updateOrganizationAsync(organization, {...organization, defaultRole})
|
||||||
|
// refreshMe is here to update the org's defaultRole in `me.organizations`
|
||||||
|
this.refreshMe()
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -77,7 +86,7 @@ OrganizationsPage.propTypes = {
|
||||||
updateOrganizationAsync: func.isRequired,
|
updateOrganizationAsync: func.isRequired,
|
||||||
deleteOrganizationAsync: func.isRequired,
|
deleteOrganizationAsync: func.isRequired,
|
||||||
}),
|
}),
|
||||||
notify: func.isRequired,
|
getMe: func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = ({links, adminChronograf: {organizations}}) => ({
|
const mapStateToProps = ({links, adminChronograf: {organizations}}) => ({
|
||||||
|
@ -87,7 +96,7 @@ const mapStateToProps = ({links, adminChronograf: {organizations}}) => ({
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
actions: bindActionCreators(adminChronografActionCreators, dispatch),
|
actions: bindActionCreators(adminChronografActionCreators, dispatch),
|
||||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
getMe: bindActionCreators(getMeAsync, dispatch),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(OrganizationsPage)
|
export default connect(mapStateToProps, mapDispatchToProps)(OrganizationsPage)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {Provider} from 'react-redux'
|
||||||
import {Router, Route, useRouterHistory} from 'react-router'
|
import {Router, Route, useRouterHistory} from 'react-router'
|
||||||
import {createHistory} from 'history'
|
import {createHistory} from 'history'
|
||||||
import {syncHistoryWithStore} from 'react-router-redux'
|
import {syncHistoryWithStore} from 'react-router-redux'
|
||||||
|
import {bindActionCreators} from 'redux'
|
||||||
|
|
||||||
import configureStore from 'src/store/configureStore'
|
import configureStore from 'src/store/configureStore'
|
||||||
import {loadLocalStorage} from 'src/localStorage'
|
import {loadLocalStorage} from 'src/localStorage'
|
||||||
|
@ -34,18 +35,9 @@ import {AdminChronografPage, AdminInfluxDBPage} from 'src/admin'
|
||||||
import {SourcePage, ManageSources} from 'src/sources'
|
import {SourcePage, ManageSources} from 'src/sources'
|
||||||
import NotFound from 'shared/components/NotFound'
|
import NotFound from 'shared/components/NotFound'
|
||||||
|
|
||||||
import {getMe} from 'shared/apis'
|
import {getMeAsync} from 'shared/actions/auth'
|
||||||
|
|
||||||
import {disablePresentationMode} from 'shared/actions/app'
|
import {disablePresentationMode} from 'shared/actions/app'
|
||||||
import {
|
|
||||||
authRequested,
|
|
||||||
authReceived,
|
|
||||||
meRequested,
|
|
||||||
meReceivedNotUsingAuth,
|
|
||||||
meReceivedUsingAuth,
|
|
||||||
logoutLinkReceived,
|
|
||||||
} from 'shared/actions/auth'
|
|
||||||
import {linksReceived} from 'shared/actions/links'
|
|
||||||
import {errorThrown} from 'shared/actions/errors'
|
import {errorThrown} from 'shared/actions/errors'
|
||||||
|
|
||||||
import 'src/style/chronograf.scss'
|
import 'src/style/chronograf.scss'
|
||||||
|
@ -87,45 +79,24 @@ const Root = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
async checkAuth() {
|
async checkAuth() {
|
||||||
dispatch(authRequested())
|
|
||||||
dispatch(meRequested())
|
|
||||||
try {
|
try {
|
||||||
await this.startHeartbeat({shouldDispatchResponse: true})
|
await this.startHeartbeat({shouldResetMe: true})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dispatch(errorThrown(error))
|
dispatch(errorThrown(error))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async startHeartbeat({shouldDispatchResponse}) {
|
getMe: bindActionCreators(getMeAsync, dispatch),
|
||||||
try {
|
|
||||||
// These non-me objects are added to every response by some AJAX trickery
|
async startHeartbeat(config) {
|
||||||
const {
|
// TODO: use destructure syntax with default {} value -- couldn't figure it out
|
||||||
data: me,
|
await this.getMe({shouldResetMe: config && config.shouldResetMe})
|
||||||
auth,
|
|
||||||
logoutLink,
|
|
||||||
external,
|
|
||||||
users,
|
|
||||||
organizations,
|
|
||||||
meLink,
|
|
||||||
} = await getMe()
|
|
||||||
if (shouldDispatchResponse) {
|
|
||||||
const isUsingAuth = !!logoutLink
|
|
||||||
dispatch(
|
|
||||||
isUsingAuth ? meReceivedUsingAuth(me) : meReceivedNotUsingAuth(me)
|
|
||||||
)
|
|
||||||
dispatch(authReceived(auth))
|
|
||||||
dispatch(logoutLinkReceived(logoutLink))
|
|
||||||
dispatch(linksReceived({external, users, organizations, me: meLink}))
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (store.getState().auth.me !== null) {
|
if (store.getState().auth.me !== null) {
|
||||||
this.startHeartbeat({shouldDispatchResponse: false})
|
this.startHeartbeat()
|
||||||
}
|
}
|
||||||
}, HEARTBEAT_INTERVAL)
|
}, HEARTBEAT_INTERVAL)
|
||||||
} catch (error) {
|
|
||||||
dispatch(errorThrown(error))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
flushErrorsQueue() {
|
flushErrorsQueue() {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import {updateMe as updateMeAJAX} from 'shared/apis/auth'
|
import {getMe as getMeAJAX, updateMe as updateMeAJAX} from 'shared/apis/auth'
|
||||||
|
|
||||||
|
import {linksReceived} from 'shared/actions/links'
|
||||||
|
|
||||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||||
import {errorThrown} from 'shared/actions/errors'
|
import {errorThrown} from 'shared/actions/errors'
|
||||||
|
@ -21,8 +23,8 @@ export const authReceived = auth => ({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export const meRequested = () => ({
|
export const meGetRequested = () => ({
|
||||||
type: 'ME_REQUESTED',
|
type: 'ME_GET_REQUESTED',
|
||||||
})
|
})
|
||||||
|
|
||||||
export const meReceivedNotUsingAuth = me => ({
|
export const meReceivedNotUsingAuth = me => ({
|
||||||
|
@ -39,6 +41,10 @@ export const meReceivedUsingAuth = me => ({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const meGetFailed = () => ({
|
||||||
|
type: 'ME_GET_FAILED',
|
||||||
|
})
|
||||||
|
|
||||||
export const meChangeOrganizationRequested = () => ({
|
export const meChangeOrganizationRequested = () => ({
|
||||||
type: 'ME_CHANGE_ORGANIZATION_REQUESTED',
|
type: 'ME_CHANGE_ORGANIZATION_REQUESTED',
|
||||||
})
|
})
|
||||||
|
@ -58,6 +64,37 @@ export const logoutLinkReceived = logoutLink => ({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// shouldResetMe protects against `me` being nullified in Redux temporarily,
|
||||||
|
// which currently causes the app to show a loading spinner until me is
|
||||||
|
// re-hydrated. if `getMeAsync` is only being used to refresh me after creating
|
||||||
|
// an organization, this is undesirable behavior
|
||||||
|
export const getMeAsync = ({shouldResetMe}) => async dispatch => {
|
||||||
|
if (shouldResetMe) {
|
||||||
|
dispatch(authRequested())
|
||||||
|
dispatch(meGetRequested())
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// These non-me objects are added to every response by some AJAX trickery
|
||||||
|
const {
|
||||||
|
data: me,
|
||||||
|
auth,
|
||||||
|
logoutLink,
|
||||||
|
external,
|
||||||
|
users,
|
||||||
|
organizations,
|
||||||
|
meLink,
|
||||||
|
} = await getMeAJAX()
|
||||||
|
const isUsingAuth = !!logoutLink
|
||||||
|
dispatch(isUsingAuth ? meReceivedUsingAuth(me) : meReceivedNotUsingAuth(me))
|
||||||
|
dispatch(authReceived(auth))
|
||||||
|
dispatch(logoutLinkReceived(logoutLink))
|
||||||
|
dispatch(linksReceived({external, users, organizations, me: meLink}))
|
||||||
|
} catch (error) {
|
||||||
|
dispatch(errorThrown(error))
|
||||||
|
dispatch(meGetFailed())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const meChangeOrganizationAsync = (
|
export const meChangeOrganizationAsync = (
|
||||||
url,
|
url,
|
||||||
organization
|
organization
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import AJAX from 'src/utils/ajax'
|
import AJAX from 'src/utils/ajax'
|
||||||
|
|
||||||
|
export function getMe() {
|
||||||
|
return AJAX({
|
||||||
|
resource: 'me',
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const updateMe = async (url, updatedMe) => {
|
export const updateMe = async (url, updatedMe) => {
|
||||||
try {
|
try {
|
||||||
return await AJAX({
|
return await AJAX({
|
||||||
|
|
|
@ -8,13 +8,6 @@ export function fetchLayouts() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMe() {
|
|
||||||
return AJAX({
|
|
||||||
resource: 'me',
|
|
||||||
method: 'GET',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSources() {
|
export function getSources() {
|
||||||
return AJAX({
|
return AJAX({
|
||||||
resource: 'sources',
|
resource: 'sources',
|
||||||
|
|
|
@ -25,7 +25,7 @@ const authReducer = (state = initialState, action) => {
|
||||||
const {auth: {links}} = action.payload
|
const {auth: {links}} = action.payload
|
||||||
return {...state, links, isAuthLoading: false}
|
return {...state, links, isAuthLoading: false}
|
||||||
}
|
}
|
||||||
case 'ME_REQUESTED': {
|
case 'ME_GET_REQUESTED': {
|
||||||
return {...state, isMeLoading: true}
|
return {...state, isMeLoading: true}
|
||||||
}
|
}
|
||||||
case 'ME_RECEIVED__NON_AUTH': {
|
case 'ME_RECEIVED__NON_AUTH': {
|
||||||
|
|
Loading…
Reference in New Issue