Stacks and Resources data storing in app state

* Normalize Stacks
* Update stack Record to contain more data
* Store Stack Resource as Immutable Record
* Extract Delployment status messages into constants

Closes-Bug: 1626481
Change-Id: I771443272881733270f8bc49724ea357002f2e28
This commit is contained in:
Jiri Tomasek 2016-09-21 16:57:09 +02:00
parent 915d907760
commit def7a4e205
11 changed files with 75 additions and 37 deletions

View File

@ -27,6 +27,9 @@ describe('StacksActions', () => {
const serviceResponse = {
stacks: [{ stack_name: 'overcloud', stack_status: 'CREATE_COMPLETE' }]
};
const normalizedStacks = {
overcloud: { stack_name: 'overcloud', stack_status: 'CREATE_COMPLETE' }
};
beforeEach(done => {
spyOn(HeatApiService, 'getStacks').and.callFake(createResolvingPromise(serviceResponse));
@ -46,7 +49,7 @@ describe('StacksActions', () => {
});
it('dispatches fetchStacksSuccess', () => {
expect(StacksActions.fetchStacksSuccess).toHaveBeenCalledWith(serviceResponse.stacks);
expect(StacksActions.fetchStacksSuccess).toHaveBeenCalledWith(normalizedStacks);
});
});

View File

@ -46,9 +46,9 @@ describe('stacksReducer state', () => {
beforeEach(() => {
state = stacksReducer(
new StacksState({ isFetching: true }),
StacksActions.fetchStacksSuccess([
{ stack_name: 'overcloud', stack_status: 'CREATE_COMPLETE' }
])
StacksActions.fetchStacksSuccess({
overcloud: { stack_name: 'overcloud', stack_status: 'CREATE_COMPLETE' }
})
);
});
@ -63,8 +63,19 @@ describe('stacksReducer state', () => {
it('sets stacks in state', () => {
expect(state.stacks).toEqualImmutable(Map({
overcloud: new Stack({
creation_time: undefined,
deletion_time: undefined,
description: undefined,
id: undefined,
parent: undefined,
resources: Map(),
stack_name: 'overcloud',
stack_status: 'CREATE_COMPLETE'
stack_owner: undefined,
stack_status: 'CREATE_COMPLETE',
stack_status_reason: undefined,
stack_user_project_id: undefined,
tags: Map(),
updated_time: undefined
})
}));
});

View File

@ -4,7 +4,7 @@ import HeatApiErrorHandler from '../services/HeatApiErrorHandler';
import HeatApiService from '../services/HeatApiService';
import NotificationActions from '../actions/NotificationActions';
import StacksConstants from '../constants/StacksConstants';
import { stackResourceSchema } from '../normalizrSchemas/stacks';
import { stackSchema, stackResourceSchema } from '../normalizrSchemas/stacks';
export default {
fetchStacksPending() {
@ -30,7 +30,8 @@ export default {
return dispatch => {
dispatch(this.fetchStacksPending());
HeatApiService.getStacks().then(response => {
dispatch(this.fetchStacksSuccess(response.stacks));
const stacks = normalize(response.stacks, arrayOf(stackSchema)).entities.stacks || {};
dispatch(this.fetchStacksSuccess(stacks));
}).catch(error => {
console.error('Error retrieving stacks StackActions.fetchStacks', error); //eslint-disable-line no-console
let errorHandler = new HeatApiErrorHandler(error);
@ -64,12 +65,12 @@ export default {
};
},
fetchResources(stackName, stackId) {
fetchResources(stack) {
return (dispatch) => {
dispatch(this.fetchResourcesPending());
HeatApiService.getResources(stackName, stackId).then((response) => {
HeatApiService.getResources(stack.stack_name, stack.id).then((response) => {
dispatch(this.fetchResourcesSuccess(
stackName,
stack.stack_name,
normalize(response.resources, arrayOf(stackResourceSchema)).entities.stackResources));
}).catch((error) => {
console.error('Error retrieving resources StackActions.fetchResources', error); //eslint-disable-line no-console

View File

@ -153,9 +153,7 @@ function mapDispatchToProps(dispatch) {
},
fetchNodes: () => dispatch(NodesActions.fetchNodes()),
fetchRoles: () => dispatch(RolesActions.fetchRoles()),
fetchResources: (name, id) => {
dispatch(StacksActions.fetchResources(name, id));
},
fetchResources: (stack) => dispatch(StacksActions.fetchResources(stack)),
fetchStacks: () => dispatch(StacksActions.fetchStacks()),
notify: notification => dispatch(NotificationActions.notify(notification)),
runValidationStage: (uuid) => {

View File

@ -1,15 +1,7 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import React from 'react';
const statusMessages = {
CREATE_IN_PROGRESS: 'Deployment in progress.',
CREATE_SUCCESS: 'Deployment succeeded.',
CREATE_FAILED: 'The deployment failed.',
DELETE_IN_PROGRESS: 'Deletion in progress.',
UPDATE_IN_PROGRESS: 'Update in progress.',
UPDATE_FAILED: 'The update failed.',
UPDATE_SUCCESS: 'The update succeeded.'
};
import { deploymentStatusMessages as statusMessages } from '../../constants/StacksConstants';
export default class DeploymentStatus extends React.Component {
@ -25,7 +17,7 @@ export default class DeploymentStatus extends React.Component {
componentWillMount() {
let intervalId = setInterval(() => {
this.props.fetchStacks();
this.props.fetchResources(this.props.stack.stack_name, this.props.stack.id);
this.props.fetchResources(this.props.stack);
}, 5000);
this.setState({ intervalId: intervalId });
}
@ -78,7 +70,9 @@ export default class DeploymentStatus extends React.Component {
return (
<div className={statusClass}>
<span className={iconClass}></span>
{msg}
<strong>{msg}</strong>
<br/>
{stack.stack_status_reason}
</div>
);
}

View File

@ -8,3 +8,14 @@ export default keyMirror({
FETCH_STACKS_SUCCESS: null,
FETCH_STACKS_FAILED: null
});
export const deploymentStatusMessages = {
CREATE_IN_PROGRESS: 'Deployment in progress',
CREATE_SUCCESS: 'Deployment succeeded',
CREATE_FAILED: 'Deployment failed',
DELETE_IN_PROGRESS: 'Deletion in progress',
DELETE_FAILED: 'Deployment deletion failed',
UPDATE_IN_PROGRESS: 'Update in progress',
UPDATE_FAILED: 'Update failed',
UPDATE_SUCCESS: 'Update succeeded'
};

View File

@ -1,4 +1,4 @@
import { Map, Record } from 'immutable';
import { List, Map, Record } from 'immutable';
export const StacksState = Record({
isLoaded: false,
@ -7,8 +7,29 @@ export const StacksState = Record({
});
export const Stack = Record({
id: '',
creation_time: undefined,
deletion_time: undefined,
description: undefined,
id: undefined,
parent: undefined,
resources: Map(),
stack_name: '',
stack_status: ''
stack_name: undefined,
stack_owner: undefined,
stack_status: undefined,
stack_status_reason: undefined,
stack_user_project_id: undefined,
tags: Map(),
updated_time: undefined
});
export const StackResource = Record({
creation_time: undefined,
logical_resource_id: undefined,
physical_resource_id: undefined,
required_by: List(),
resource_name: undefined,
resource_status: undefined,
resource_status_reason: undefined,
resource_type: undefined,
updated_time: undefined
});

View File

@ -1,3 +1,4 @@
import { Schema } from 'normalizr';
export const stackSchema = new Schema('stacks', { idAttribute: 'stack_name' });
export const stackResourceSchema = new Schema('stackResources', { idAttribute: 'resource_name' });

View File

@ -1,6 +1,6 @@
import { fromJS, Map } from 'immutable';
import { Stack, StacksState } from '../immutableRecords/stacks';
import { Stack, StackResource, StacksState } from '../immutableRecords/stacks';
import StacksConstants from '../constants/StacksConstants';
const initialState = new StacksState;
@ -15,10 +15,7 @@ export default function stacksReducer(state = initialState, action) {
return state
.set('isLoaded', true)
.set('isFetching', false)
.set('stacks', Map(action.payload.reduce((obj, val) => {
obj[val.stack_name] = Stack(val);
return obj;
}, {})));
.set('stacks', fromJS(action.payload).map(stack => new Stack(stack)));
case StacksConstants.FETCH_STACKS_FAILED:
return state
@ -28,8 +25,10 @@ export default function stacksReducer(state = initialState, action) {
case StacksConstants.FETCH_RESOURCES_SUCCESS:
if (state.stacks.get(action.payload.stackName)) {
return state.setIn(['stacks', action.payload.stackName, 'resources'],
fromJS(action.payload.resources));
return state.setIn(
['stacks', action.payload.stackName, 'resources'],
fromJS(action.payload.resources).map(resource => new StackResource(resource))
);
}
return state;

View File

@ -21,6 +21,6 @@ export const getCurrentStack = createSelector(
export const getCurrentStackDeploymentProgress = createSelector(
[stacksSelector, currentPlanNameSelector],
(stacks, currentPlanName) => {
return stacks.get(currentPlanName, Stack({})).stack_status === 'CREATE_IN_PROGRESS';
return stacks.get(currentPlanName, new Stack()).stack_status === 'CREATE_IN_PROGRESS';
}
);

View File

@ -31,7 +31,6 @@ class HeatApiService {
getResources(stackName, stackId) {
return this.defaultRequest(`/stacks/${stackName}/${stackId}/resources`);
}
}
export default new HeatApiService();