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

Entity deletion: Remove child entities #2486

Merged
merged 23 commits into from
Jun 26, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
288818d
WIP Entity deletion traversal
KlapTrap Jun 18, 2018
4b8c0d4
Entity traversal to flat tree of ids
KlapTrap Jun 19, 2018
3f813bc
WIP Fixes for none schema object
KlapTrap Jun 20, 2018
558cf54
Better tests and fixed issue
KlapTrap Jun 20, 2018
c946bc8
Wire in deleting into request reducer
KlapTrap Jun 20, 2018
1ed6120
Merge branch 'v2-master' into entity-deletion
KlapTrap Jun 20, 2018
5579c54
WIP Set entities as deleted
KlapTrap Jun 20, 2018
741c0b3
Ensure we tidy up related entities when recusivly deleting
KlapTrap Jun 21, 2018
845e6a8
Add mechanism to call entity specific actions on delete
KlapTrap Jun 21, 2018
74e88f1
Test fix
KlapTrap Jun 21, 2018
d5478cf
Merge branch 'v2-master' into entity-deletion
KlapTrap Jun 21, 2018
df773e9
Merge remote-tracking branch 'origin/v2-master' into entity-deletion
richard-cox Jun 22, 2018
27c3e4e
Removed review todo, rearranged imports
richard-cox Jun 22, 2018
2dc2062
Ensure deleting state is reset once same entity is refetched
richard-cox Jun 25, 2018
3ce8f47
Merge branch 'v2-master' into entity-deletion
KlapTrap Jun 25, 2018
8a06486
Fixed delete failed and added exclude list
KlapTrap Jun 25, 2018
aacf020
Don't traverse through schema of excluded entity
KlapTrap Jun 25, 2018
bb7aa00
Remove app delete recursive param - We fine grain this now
richard-cox Jun 25, 2018
8d2cabc
Add excludes to EntitySchemaTreeBuilder
richard-cox Jun 25, 2018
42f5bd1
Re-add resursive, other minor fixes
richard-cox Jun 25, 2018
c09c62a
Fix typo
richard-cox Jun 25, 2018
628af48
Throw recursive delete failed on cf error
KlapTrap Jun 26, 2018
c68dadf
Update tests to include excluded entities
KlapTrap Jun 26, 2018
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
Prev Previous commit
Next Next commit
Fixed delete failed and added exclude list
  • Loading branch information
KlapTrap committed Jun 25, 2018
commit 8a064868e6b6a87d8dd0e3561c574afd9d6d05bc
14 changes: 11 additions & 3 deletions src/frontend/app/store/effects/api.effects.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { Headers, Http, Request, URLSearchParams } from '@angular/http';
import { Actions, Effect } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { Store, Action } from '@ngrx/store';
import { normalize, Schema } from 'normalizr';
import { forkJoin, Observable, of as observableOf } from 'rxjs';
import { catchError, map, mergeMap, withLatestFrom } from 'rxjs/operators';
Expand All @@ -24,7 +24,7 @@ import { ApiActionTypes, ValidateEntitiesStart } from './../actions/request.acti
import { AppState, IRequestEntityTypeState } from './../app-state';
import { APIResource, instanceOfAPIResource, NormalizedResponse } from './../types/api.types';
import { StartRequestAction, WrapperRequestActionFailed } from './../types/request.types';
import { RecursiveDelete, RecursiveDeleteComplete } from './recursive-entity-delete.effect';
import { RecursiveDelete, RecursiveDeleteComplete, RecursiveDeleteFailed } from './recursive-entity-delete.effect';


const { proxyAPIVersion, cfAPIVersion } = environment;
Expand Down Expand Up @@ -180,14 +180,22 @@ export class APIEffect {
url: error.url || apiAction.options.url
}
})));
return [
const errorActions: Action[] = [
new APISuccessOrFailedAction(actionClone.actions[2], actionClone, error.message),
new WrapperRequestActionFailed(
error.message,
actionClone,
requestType
)
];
if (requestType === 'delete') {
errorActions.push(new RecursiveDeleteFailed(
apiAction.guid,
apiAction.endpointGuid,
entityFactory(apiAction.entityKey)
));
}
return errorActions;
}));
}

Expand Down
32 changes: 27 additions & 5 deletions src/frontend/app/store/effects/recursive-entity-delete.effect.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';
import { Action, Store } from '@ngrx/store';
import { schema } from 'normalizr';
import { map, withLatestFrom, mergeMap } from 'rxjs/operators';
import { map, mergeMap, withLatestFrom } from 'rxjs/operators';

import { DELETE_SUCCESS, DeleteApplication } from '../actions/application.actions';
import { ClearPaginationOfType } from '../actions/pagination.actions';
import { AppState } from '../app-state';
import { EntitySchema, applicationSchemaKey } from '../helpers/entity-factory';
import { EntitySchema } from '../helpers/entity-factory';
import { EntitySchemaTreeBuilder, IFlatTree } from '../helpers/schema-tree-traverse';
import { getAPIRequestDataState } from '../selectors/api.selectors';
import { IRequestDataState } from '../types/entity.types';
import { ClearPaginationOfEntity, ClearPaginationOfType } from '../actions/pagination.actions';
import { APISuccessOrFailedAction, ICFAction } from '../types/request.types';
import { DELETE_SUCCESS, DeleteApplication } from '../actions/application.actions';


export const RECURSIVE_ENTITY_DELETE = '[Entity] Recursive entity delete';
export const RECURSIVE_ENTITY_DELETE_COMPLETE = '[Entity] Recursive entity delete complete';
export const RECURSIVE_ENTITY_DELETE_FAILED = '[Entity] Recursive entity delete failed';

export const RECURSIVE_ENTITY_RESET = '[Entity] Recursive entity reset';
export const RECURSIVE_ENTITY_SET_DELETING = '[Entity] Recursive entity set deleting';
export const RECURSIVE_ENTITY_SET_DELETED = '[Entity] Recursive entity set deleted';

Expand All @@ -34,6 +36,11 @@ export class RecursiveDeleteComplete implements Action, IRecursiveDelete {
constructor(public guid: string, public endpointGuid: string, public schema: EntitySchema) { }
}

export class RecursiveDeleteFailed implements Action, IRecursiveDelete {
public type = RECURSIVE_ENTITY_DELETE_FAILED;
constructor(public guid: string, public endpointGuid: string, public schema: EntitySchema) { }
}

export class SetTreeDeleting implements Action {
public type = RECURSIVE_ENTITY_SET_DELETING;
constructor(public parentGuid: string, public tree: IFlatTree) { }
Expand All @@ -44,6 +51,11 @@ export class SetTreeDeleted implements Action {
constructor(public parentGuid: string, public tree: IFlatTree) { }
}

export class ResetTreeDelete implements Action {
public type = RECURSIVE_ENTITY_RESET;
constructor(public parentGuid: string, public tree: IFlatTree) { }
}

@Injectable()
export class RecursiveDeleteEffect {
private entityTreeCache: { [guid: string]: IFlatTree } = {};
Expand All @@ -63,6 +75,7 @@ export class RecursiveDeleteEffect {
withLatestFrom(this.store.select(getAPIRequestDataState)),
map(([action, state]) => {
const tree = this.getTree(action, state);
console.log(tree);
return new SetTreeDeleting(action.guid, tree);
})
);
Expand All @@ -85,6 +98,15 @@ export class RecursiveDeleteEffect {
})
);

@Effect()
deleteFailed$ = this.actions$.ofType<RecursiveDeleteFailed>(RECURSIVE_ENTITY_DELETE_FAILED).pipe(
withLatestFrom(this.store.select(getAPIRequestDataState)),
map(([action, state]) => {
const tree = this.getTree(action, state);
return new ResetTreeDelete(action.guid, tree);
})
);

private getTree(action: IRecursiveDelete, state: IRequestDataState) {
const tree = this.entityTreeCache[action.guid] ?
this.entityTreeCache[action.guid] :
Expand Down
35 changes: 27 additions & 8 deletions src/frontend/app/store/helpers/schema-tree-traverse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,33 @@ import { denormalize } from 'normalizr';

import { IRecursiveDelete } from '../effects/recursive-entity-delete.effect';
import { IRequestDataState } from '../types/entity.types';
import { EntitySchema } from './entity-factory';
import { EntitySchema, spaceSchemaKey, organizationSchemaKey, applicationSchemaKey, routeSchemaKey } from './entity-factory';

export interface IFlatTree {
[entityKey: string]: Set<string>;
}
type TEntityIds = Set<string>;

interface IExcludes {
[entityKey: string]: string[];
}

export class EntitySchemaTreeBuilder {
private excludes: IExcludes = {
[spaceSchemaKey]: [
organizationSchemaKey
],
[applicationSchemaKey]: [
organizationSchemaKey,
routeSchemaKey,
spaceSchemaKey
]
};

private entityExcludes: string[];
public getFlatTree(treeDefinition: IRecursiveDelete, state: Partial<IRequestDataState>): IFlatTree {
const { schema, guid } = treeDefinition;
const denormed = denormalize(guid, schema, state);
this.entityExcludes = this.excludes[schema.key] || [];
return this.build(schema, denormed, undefined, true);
}

Expand Down Expand Up @@ -46,7 +63,7 @@ export class EntitySchemaTreeBuilder {
}
// Don't add the root element to the tree to avoid duplication actions whe consuming tree
if (!root) {
flatTree[schema.key] = this.addIdToTree(flatTree[schema.key], schema.getId(entity));
flatTree = this.addIdToTree(flatTree, schema.key, schema.getId(entity));
}
if (!keys) {
return flatTree;
Expand All @@ -62,11 +79,13 @@ export class EntitySchemaTreeBuilder {
}, flatTree);
}

private addIdToTree(ids: TEntityIds, newId: string) {
if (!ids) {
ids = new Set<string>();
private addIdToTree(flatTree: IFlatTree, key: string, newId: string) {
if (this.entityExcludes.includes(key)) {
return flatTree;
}
return ids.add(newId);
const ids = flatTree[key] || new Set<string>();
flatTree[key] = ids.add(newId);
return flatTree;
}

private getDefinition(definition) {
Expand All @@ -91,7 +110,7 @@ export class EntitySchemaTreeBuilder {
}
return flatTree;
}
flatTree[key] = this.addIdToTree(flatTree[key], id);
flatTree = this.addIdToTree(flatTree, key, id);
const subKeys = Object.keys(entityDefinition);
if (subKeys.length > 0) {
return this.build(entityDefinition, entity, flatTree);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ export function createRequestStateFromResponse(response: NormalizedResponse, sta
entState.fetching = false;
entState.error = false;
const busy = entState.deleting ? entState.deleting.busy : false;
entState.deleting = {
...defaultDeletingActionState,
busy
};
entState.deleting = { ...defaultDeletingActionState, busy };
newState = setEntityRequestState(newState, entState, { entityKey, guid } as IRequestAction);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@ import { startRequest } from './start-request';
import { succeedRequest } from './succeed-request';
import { IRequestArray } from './types';
import { updateRequest } from './update-request';
import { RECURSIVE_ENTITY_SET_DELETING, RECURSIVE_ENTITY_SET_DELETED } from '../../effects/recursive-entity-delete.effect';
import { setChildEntitiesAsDeleting, setChildEntitiesAsDeleted } from './deleting-child-entities';
import {
RECURSIVE_ENTITY_SET_DELETING,
RECURSIVE_ENTITY_SET_DELETED,
RECURSIVE_ENTITY_RESET,
RECURSIVE_ENTITY_DELETE_FAILED
} from '../../effects/recursive-entity-delete.effect';
import { setChildEntitiesAsDeleting, setChildEntitiesAsDeleted, resetChildEntities } from './deleting-child-entities';
import { StartRequestAction } from '../../types/request.types';

export function requestReducerFactory(entityList = [], actions: IRequestArray) {
const [startAction, successAction, failedAction, updateAction] = actions;
const defaultState = generateDefaultState(entityList);
return function apiRequestReducer(state = defaultState, action) {
switch (action.type) {
case startAction:
return startRequest(state, action);
return startRequest(state, action as StartRequestAction);
case successAction:
return succeedRequest(state, action);
case failedAction:
Expand All @@ -22,6 +28,8 @@ export function requestReducerFactory(entityList = [], actions: IRequestArray) {
return updateRequest(state, action);
case RECURSIVE_ENTITY_SET_DELETING:
return setChildEntitiesAsDeleting(state, action);
case RECURSIVE_ENTITY_RESET:
return resetChildEntities(state, action);
case RECURSIVE_ENTITY_SET_DELETED:
return setChildEntitiesAsDeleted(state, action);
default:
Expand Down