web: break the reducers module into logical units

This change applies best practices to split the current reducers module
in logical unit. Each reducer and its actions are moved into different
modules to ease further refactor and follow-up tests.

Change-Id: I75cc41ca3d31a61046868aafbc84505de661a99d
This commit is contained in:
Tristan Cacqueray 2018-12-02 01:05:02 +00:00
parent 300915538c
commit 76867ca14a
14 changed files with 218 additions and 124 deletions

View File

@ -29,7 +29,8 @@ import {
import logo from './images/logo.png'
import { routes } from './routes'
import { fetchConfigErrorsAction, setTenantAction } from './reducers'
import { fetchConfigErrorsAction } from './actions/configErrors'
import { setTenantAction } from './actions/tenant'
class App extends React.Component {

View File

@ -19,7 +19,8 @@ import ReactDOM from 'react-dom'
import { Link, BrowserRouter as Router } from 'react-router-dom'
import { Provider } from 'react-redux'
import { createZuulStore, fetchInfoAction } from './reducers'
import { fetchInfoAction } from './actions/info'
import createZuulStore from './store'
import App from './App'
import TenantsPage from './pages/Tenants'
import StatusPage from './pages/Status'

View File

@ -0,0 +1,28 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
import { fetchConfigErrors } from '../api'
export function fetchConfigErrorsAction (tenant) {
return (dispatch) => {
return fetchConfigErrors(tenant.apiPrefix)
.then(response => {
dispatch({type: 'FETCH_CONFIGERRORS_SUCCESS',
errors: response.data})
})
.catch(error => {
throw (error)
})
}
}

27
web/src/actions/info.js Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
import { fetchInfo } from '../api'
export function fetchInfoAction () {
return (dispatch) => {
return fetchInfo()
.then(response => {
dispatch({type: 'FETCH_INFO_SUCCESS', info: response.data.info})
})
.catch(error => {
throw (error)
})
}
}

37
web/src/actions/tenant.js Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
export function setTenantAction (name, whiteLabel) {
let apiPrefix = ''
let linkPrefix = ''
let routePrefix = ''
let defaultRoute = '/status'
if (!whiteLabel) {
apiPrefix = 'tenant/' + name + '/'
linkPrefix = '/t/' + name
routePrefix = '/t/:tenant'
defaultRoute = '/tenants'
}
return {
type: 'SET_TENANT',
tenant: {
name: name,
whiteLabel: whiteLabel,
defaultRoute: defaultRoute,
linkPrefix: linkPrefix,
apiPrefix: apiPrefix,
routePrefix: routePrefix
}
}
}

View File

@ -18,7 +18,8 @@ import ReactTestUtils from 'react-dom/test-utils'
import { Link, BrowserRouter as Router } from 'react-router-dom'
import { Provider } from 'react-redux'
import { createZuulStore, setTenantAction } from '../../reducers'
import { setTenantAction } from '../../actions/tenant'
import createZuulStore from '../../store'
import ChangePanel from './ChangePanel'

View File

@ -25,12 +25,13 @@ import './index.css'
import { getHomepageUrl } from './api'
import registerServiceWorker from './registerServiceWorker'
import { createZuulStore, fetchInfoAction } from './reducers'
import { fetchInfoAction } from './actions/info'
import createZuulStore from './store'
import App from './App'
// This calls the /api/info endpoint asynchronously, the App is connected
// with redux and it will update the info prop when fetch succeed.
const store = createZuulStore()
// Load info endpoint
store.dispatch(fetchInfoAction())
ReactDOM.render(

View File

@ -19,7 +19,7 @@ import {
Icon
} from 'patternfly-react'
import { fetchConfigErrorsAction } from '../reducers'
import { fetchConfigErrorsAction } from '../actions/configErrors'
class ConfigErrorsPage extends React.Component {
static propTypes = {

View File

@ -1,117 +0,0 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// Redux store enable to share global variables through state
// To update the store, use a reducer and dispatch method,
// see the App.setTenant method
//
// The store contains:
// info: the info object, tenant is set when white-label api
// tenant: the current tenant name, only used with multi-tenant api
import { applyMiddleware, createStore, combineReducers } from 'redux'
import thunk from 'redux-thunk'
import { fetchConfigErrors, fetchInfo } from './api'
const infoReducer = (state = {}, action) => {
switch (action.type) {
case 'FETCH_INFO_SUCCESS':
return action.info
default:
return state
}
}
const configErrorsReducer = (state = [], action) => {
switch (action.type) {
case 'FETCH_CONFIGERRORS_SUCCESS':
return action.errors
default:
return state
}
}
const tenantReducer = (state = {}, action) => {
switch (action.type) {
case 'SET_TENANT':
return action.tenant
default:
return state
}
}
function createZuulStore() {
return createStore(combineReducers({
info: infoReducer,
tenant: tenantReducer,
configErrors: configErrorsReducer,
}), applyMiddleware(thunk))
}
// Reducer actions
function fetchInfoAction () {
return (dispatch) => {
return fetchInfo()
.then(response => {
dispatch({type: 'FETCH_INFO_SUCCESS', info: response.data.info})
})
.catch(error => {
throw (error)
})
}
}
function fetchConfigErrorsAction (tenant) {
return (dispatch) => {
return fetchConfigErrors(tenant.apiPrefix)
.then(response => {
dispatch({type: 'FETCH_CONFIGERRORS_SUCCESS',
errors: response.data})
})
.catch(error => {
throw (error)
})
}
}
function setTenantAction (name, whiteLabel) {
let apiPrefix = ''
let linkPrefix = ''
let routePrefix = ''
let defaultRoute = '/status'
if (!whiteLabel) {
apiPrefix = 'tenant/' + name + '/'
linkPrefix = '/t/' + name
routePrefix = '/t/:tenant'
defaultRoute = '/tenants'
}
return {
type: 'SET_TENANT',
tenant: {
name: name,
whiteLabel: whiteLabel,
defaultRoute: defaultRoute,
linkPrefix: linkPrefix,
apiPrefix: apiPrefix,
routePrefix: routePrefix
}
}
}
export {
createZuulStore,
setTenantAction,
fetchConfigErrorsAction,
fetchInfoAction
}

View File

@ -0,0 +1,22 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_CONFIGERRORS_SUCCESS':
return action.errors
default:
return state
}
}

27
web/src/reducers/index.js Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
import { combineReducers } from 'redux'
import configErrors from './configErrors'
import info from './info'
import tenant from './tenant'
const reducers = {
info,
configErrors,
tenant,
}
export default combineReducers(reducers)

22
web/src/reducers/info.js Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
export default (state = {}, action) => {
switch (action.type) {
case 'FETCH_INFO_SUCCESS':
return action.info
default:
return state
}
}

View File

@ -0,0 +1,22 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
export default (state = {}, action) => {
switch (action.type) {
case 'SET_TENANT':
return action.tenant
default:
return state
}
}

22
web/src/store.js Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2018 Red Hat, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
import { applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'
import appReducers from './reducers'
export default function createZuulStore() {
return createStore(appReducers, applyMiddleware(thunk))
}