web: refactor info and tenant reducers action

This change adds info fetch state action type and simplifies the main App
by using the new info attributes.

Change-Id: I2cfd3f6ae605051e11f58272e62925d8f97e4ac9
This commit is contained in:
Tristan Cacqueray 2018-12-02 01:46:43 +00:00
parent 76867ca14a
commit 17144c2a46
7 changed files with 85 additions and 31 deletions

View File

@ -86,8 +86,12 @@ class App extends React.Component {
} }
renderContent = () => { renderContent = () => {
const { tenant } = this.props const { info, tenant } = this.props
const allRoutes = [] const allRoutes = []
if (info.isFetching) {
return (<h2>Fetching info...</h2>)
}
this.menu this.menu
// Do not include '/tenants' route in white-label setup // Do not include '/tenants' route in white-label setup
.filter(item => .filter(item =>
@ -102,10 +106,13 @@ class App extends React.Component {
/> />
) )
}) })
if (tenant.defaultRoute)
allRoutes.push(
<Redirect from='*' to={tenant.defaultRoute} key='default-route' />
)
return ( return (
<Switch> <Switch>
{allRoutes} {allRoutes}
<Redirect from='*' to={tenant.defaultRoute} key='default-route' />
</Switch> </Switch>
) )
} }
@ -113,7 +120,7 @@ class App extends React.Component {
componentDidUpdate() { componentDidUpdate() {
// This method is called when info property is updated // This method is called when info property is updated
const { tenant, info } = this.props const { tenant, info } = this.props
if (info.capabilities) { if (info.ready) {
let tenantName, whiteLabel let tenantName, whiteLabel
if (info.tenant) { if (info.tenant) {
@ -129,12 +136,10 @@ class App extends React.Component {
if (match) { if (match) {
tenantName = match.params.tenant tenantName = match.params.tenant
} else {
tenantName = ''
} }
} }
// Set tenant only if it changed to prevent DidUpdate loop // Set tenant only if it changed to prevent DidUpdate loop
if (typeof tenant.name === 'undefined' || tenant.name !== tenantName) { if (tenant.name !== tenantName) {
const tenantAction = setTenantAction(tenantName, whiteLabel) const tenantAction = setTenantAction(tenantName, whiteLabel)
this.props.dispatch(tenantAction) this.props.dispatch(tenantAction)
if (tenantName) { if (tenantName) {
@ -204,10 +209,6 @@ class App extends React.Component {
const { menuCollapsed, showErrors } = this.state const { menuCollapsed, showErrors } = this.state
const { tenant, configErrors } = this.props const { tenant, configErrors } = this.props
if (typeof tenant.name === 'undefined') {
return (<h2>Loading...</h2>)
}
return ( return (
<React.Fragment> <React.Fragment>
<Masthead <Masthead

View File

@ -19,7 +19,7 @@ import ReactDOM from 'react-dom'
import { Link, BrowserRouter as Router } from 'react-router-dom' import { Link, BrowserRouter as Router } from 'react-router-dom'
import { Provider } from 'react-redux' import { Provider } from 'react-redux'
import { fetchInfoAction } from './actions/info' import { fetchInfoIfNeeded } from './actions/info'
import createZuulStore from './store' import createZuulStore from './store'
import App from './App' import App from './App'
import TenantsPage from './pages/Tenants' import TenantsPage from './pages/Tenants'
@ -54,7 +54,7 @@ it('renders multi tenant', () => {
const application = ReactTestUtils.renderIntoDocument( const application = ReactTestUtils.renderIntoDocument(
<Provider store={store}><Router><App /></Router></Provider> <Provider store={store}><Router><App /></Router></Provider>
) )
store.dispatch(fetchInfoAction()).then(() => { store.dispatch(fetchInfoIfNeeded()).then(() => {
// Link should be tenant scoped // Link should be tenant scoped
const topMenuLinks = ReactTestUtils.scryRenderedComponentsWithType( const topMenuLinks = ReactTestUtils.scryRenderedComponentsWithType(
application, Link) application, Link)
@ -86,7 +86,7 @@ it('renders single tenant', () => {
<Provider store={store}><Router><App /></Router></Provider> <Provider store={store}><Router><App /></Router></Provider>
) )
store.dispatch(fetchInfoAction()).then(() => { store.dispatch(fetchInfoIfNeeded()).then(() => {
// Link should be white-label scoped // Link should be white-label scoped
const topMenuLinks = ReactTestUtils.scryRenderedComponentsWithType( const topMenuLinks = ReactTestUtils.scryRenderedComponentsWithType(
application, Link) application, Link)

View File

@ -12,16 +12,46 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
import { fetchInfo } from '../api' import * as API from '../api'
export function fetchInfoAction () { export const INFO_FETCH_REQUEST = 'INFO_FETCH_REQUEST'
return (dispatch) => { export const INFO_FETCH_SUCCESS = 'INFO_FETCH_SUCCESS'
return fetchInfo() export const INFO_FETCH_FAIL = 'INFO_FETCH_FAIL'
.then(response => {
dispatch({type: 'FETCH_INFO_SUCCESS', info: response.data.info}) export const fetchInfoRequest = () => ({
}) type: INFO_FETCH_REQUEST
.catch(error => { })
throw (error)
}) export const fetchInfoSuccess = json => ({
type: INFO_FETCH_SUCCESS,
tenant: json.info.tenant,
})
const fetchInfoFail = error => ({
type: INFO_FETCH_FAIL,
error
})
const fetchInfo = () => dispatch => {
dispatch(fetchInfoRequest())
return API.fetchInfo()
.then(response => dispatch(fetchInfoSuccess(response.data)))
.catch(error => dispatch(fetchInfoFail(error)))
}
const shouldFetchInfo = state => {
const info = state.info
if (!info) {
return true
}
if (info.isFetching) {
return false
}
return true
}
export const fetchInfoIfNeeded = () => (dispatch, getState) => {
if (shouldFetchInfo(getState())) {
return dispatch(fetchInfo())
} }
} }

View File

@ -12,6 +12,8 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
export const TENANT_SET = 'TENANT_SET'
export function setTenantAction (name, whiteLabel) { export function setTenantAction (name, whiteLabel) {
let apiPrefix = '' let apiPrefix = ''
let linkPrefix = '' let linkPrefix = ''
@ -24,7 +26,7 @@ export function setTenantAction (name, whiteLabel) {
defaultRoute = '/tenants' defaultRoute = '/tenants'
} }
return { return {
type: 'SET_TENANT', type: TENANT_SET,
tenant: { tenant: {
name: name, name: name,
whiteLabel: whiteLabel, whiteLabel: whiteLabel,

View File

@ -25,14 +25,14 @@ import './index.css'
import { getHomepageUrl } from './api' import { getHomepageUrl } from './api'
import registerServiceWorker from './registerServiceWorker' import registerServiceWorker from './registerServiceWorker'
import { fetchInfoAction } from './actions/info' import { fetchInfoIfNeeded } from './actions/info'
import createZuulStore from './store' import createZuulStore from './store'
import App from './App' import App from './App'
const store = createZuulStore() const store = createZuulStore()
// Load info endpoint // Load info endpoint
store.dispatch(fetchInfoAction()) store.dispatch(fetchInfoIfNeeded())
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>

View File

@ -12,10 +12,29 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
export default (state = {}, action) => { import {
INFO_FETCH_REQUEST,
INFO_FETCH_SUCCESS,
INFO_FETCH_FAIL,
} from '../actions/info'
export default (state = {
isFetching: false,
tenant: null,
}, action) => {
switch (action.type) { switch (action.type) {
case 'FETCH_INFO_SUCCESS': case INFO_FETCH_REQUEST:
return action.info case INFO_FETCH_FAIL:
return {
isFetching: true,
tenant: null,
}
case INFO_FETCH_SUCCESS:
return {
isFetching: false,
tenant: action.tenant,
ready: true
}
default: default:
return state return state
} }

View File

@ -12,9 +12,11 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
export default (state = {}, action) => { import { TENANT_SET } from '../actions/tenant'
export default (state = {name: null}, action) => {
switch (action.type) { switch (action.type) {
case 'SET_TENANT': case TENANT_SET:
return action.tenant return action.tenant
default: default:
return state return state