Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix null exception after creating a space in an new org #3351

Merged
merged 2 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/frontend/app/core/cf-api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export interface IDeveloper {
audited_spaces_url: string;
}

export interface IOrganization {
export interface IOrganization<spaceT = APIResource<ISpace>[]> {
name: string;
billing_enabled?: boolean;
quota_definition_guid?: string;
Expand All @@ -176,7 +176,7 @@ export interface IOrganization {
space_quota_definitions_url?: string;
guid?: string;
cfGuid?: string;
spaces?: APIResource<ISpace>[];
spaces?: spaceT;
private_domains?: APIResource<IPrivateDomain>[];
quota_definition?: APIResource<IQuotaDefinition>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Observable, Subscription } from 'rxjs';
import { filter, map, take, tap } from 'rxjs/operators';

import { IOrganization } from '../../../../core/cf-api.types';
import { safeUnsubscribe } from '../../../../core/utils.service';
import { StepOnNextFunction } from '../../../../shared/components/stepper/step/step.component';
import { PaginationMonitorFactory } from '../../../../shared/monitors/pagination-monitor.factory';
import { UpdateOrganization } from '../../../../store/actions/organization.actions';
Expand Down Expand Up @@ -126,6 +127,6 @@ export class EditOrganizationStepComponent implements OnInit, OnDestroy {
}

ngOnDestroy(): void {
this.fetchOrgsSub.unsubscribe();
safeUnsubscribe(this.fetchOrgsSub, this.orgSubscription);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ export class CloudFoundryEndpointService {
this.allApps$ = pagObs.entities$.pipe(// Ensure we sub to entities to kick off fetch process
switchMap(() => pagObs.pagination$),
filter(pagination => !!pagination && !!pagination.pageRequests && !!pagination.pageRequests[1] && !pagination.pageRequests[1].busy),
switchMap(pagination => pagination.maxedResults ? observableOf(null) : pagObs.entities$)
switchMap(pagination => pagination.maxedResults ? observableOf(null) : pagObs.entities$),
publishReplay(1),
refCount()
);

this.loadingApps$ = pagObs.entities$.pipe(// Ensure we sub to entities to kick off fetch process
Expand Down
115 changes: 74 additions & 41 deletions src/frontend/app/store/reducers/organization-space.reducer.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,89 @@
import { IOrganization, ISpace } from '../../core/cf-api.types';
import { BaseSpaceAction, CREATE_SPACE_SUCCESS, DELETE_SPACE_SUCCESS } from '../actions/space.actions';
import { APIResource } from '../types/api.types';
import {
BaseSpaceAction,
CREATE_SPACE_SUCCESS,
CreateSpace,
DELETE_SPACE_SUCCESS,
DeleteSpace,
} from '../actions/space.actions';
import { spaceSchemaKey } from '../helpers/entity-factory';
import { APIResource, NormalizedResponse } from '../types/api.types';
import { APISuccessOrFailedAction } from '../types/request.types';

// Note - This reducer will be updated when we address general entity relation deletion
import { IRequestEntityTypeState } from '../app-state';
type entityOrgType = APIResource<IOrganization<string[]>>;
// Note - This reducer will be updated when we address general deletion of entities within inline lists (not paginated lists)
export function updateOrganizationSpaceReducer() {
return function (state: APIResource, action: APISuccessOrFailedAction) {
return function (state: IRequestEntityTypeState<entityOrgType>, action: APISuccessOrFailedAction<NormalizedResponse>) {
switch (action.type) {
case DELETE_SPACE_SUCCESS:
const deleteSpaceAction: DeleteSpace = action.apiAction as DeleteSpace;
return removeSpaceFromOrg(state, deleteSpaceAction.orgGuid, deleteSpaceAction.guid);
case CREATE_SPACE_SUCCESS:
const spaceAction: BaseSpaceAction = action.apiAction as BaseSpaceAction;
return deleteOrgSpaces(state, spaceAction.orgGuid, spaceAction);
const createSpaceAction = action.apiAction as CreateSpace;
const response = action.response;
const space = response.entities[spaceSchemaKey][response.result[0]];
return addSpaceToOrg(state, createSpaceAction.orgGuid, space);
}
return state;
};
}

function deleteOrgSpaces(state: APIResource, orgGuid: string, spaceAction: BaseSpaceAction) {
if (!orgGuid) {
return state;
}
function addSpaceToOrg(
state: IRequestEntityTypeState<entityOrgType>,
orgGuid: string,
newSpace: APIResource<ISpace>
) {
const orgToModify = getOrg(state, orgGuid);
const newSpaces = [
...orgToModify.entity.spaces,
newSpace.metadata.guid
];
const mergedOrg = applySpacesToOrg(orgToModify, newSpaces);
return {
...state,
[orgGuid]: mergedOrg
};
}

const orgGuids = Object.keys(state);
if (orgGuids.indexOf(orgGuid) === -1) {
return state;
}
function removeSpaceFromOrg(
state: IRequestEntityTypeState<entityOrgType>,
orgGuid: string,
spaceGuid: string
) {
const orgToModify = getOrg(state, orgGuid);
const newSpaces = orgToModify.entity.spaces.reduce((spaceIds, spaceId) => {
if (spaceId !== spaceGuid) {
spaceIds.push(spaceId);
}
return spaceIds;
}, []);
const mergedOrg = applySpacesToOrg(orgToModify, newSpaces);
return applyModifyOrgToState(state, mergedOrg);
}

const newState = {};
orgGuids.forEach(currentGuid => {
const org: APIResource<IOrganization> = state[currentGuid];
if (currentGuid === orgGuid) {
let newSpaces: APIResource<ISpace>[] = null;
if (spaceAction.removeEntityOnDelete) {
const spaceIndex = org.entity.spaces.findIndex(space => {
return typeof (space) === 'string' ? space === spaceAction.guid : space.metadata.guid === spaceAction.guid;
});
if (spaceIndex >= 0) {
newSpaces = [...org.entity.spaces];
newSpaces.splice(spaceIndex, 1);
}
}
const newOrg = {
...org,
entity: {
...org.entity,
spaces: newSpaces
}
};
newState[currentGuid] = newOrg;
} else {
newState[currentGuid] = org;
function applySpacesToOrg(org: entityOrgType, spaces: string[]): entityOrgType {
return {
...org,
entity: {
...org.entity,
spaces
}
});
return newState;
};
}

function applyModifyOrgToState(state: IRequestEntityTypeState<entityOrgType>, org: entityOrgType) {
return {
...state,
[org.metadata.guid]: org
};
}

function getOrg(
state: IRequestEntityTypeState<entityOrgType>,
orgGuid: string,
) {
const {
[orgGuid]: newOrg
} = state;
return newOrg;
}