diff --git a/scm-ui/src/groups/modules/groups.js b/scm-ui/src/groups/modules/groups.js index a3a5aa1bdb..b91c8f2eb4 100644 --- a/scm-ui/src/groups/modules/groups.js +++ b/scm-ui/src/groups/modules/groups.js @@ -168,6 +168,54 @@ export function createGroupFailure(error: Error) { }; } +//delete group + +export function deleteGroup(group: Group, callback?: () => void) { + return function(dispatch: any) { + dispatch(deleteGroupPending(group)); + return apiClient + .delete(group._links.delete.href) + .then(() => { + dispatch(deleteGroupSuccess(group)); + if (callback) { + callback(); + } + }) + .catch(cause => { + const error = new Error( + `could not delete group ${group.name}: ${cause.message}` + ); + dispatch(deleteGroupFailure(group, error)); + }); + }; +} + +export function deleteGroupPending(group: Group): Action { + return { + type: DELETE_GROUP_PENDING, + payload: group, + itemId: group.name + }; +} + +export function deleteGroupSuccess(group: Group): Action { + return { + type: DELETE_GROUP_SUCCESS, + payload: group, + itemId: group.name + }; +} + +export function deleteGroupFailure(group: Group, error: Error): Action { + return { + type: DELETE_GROUP_FAILURE, + payload: { + error, + group + }, + itemId: group.name + }; +} //reducer function extractGroupsByNames( @@ -187,6 +235,22 @@ function extractGroupsByNames( return groupsByNames; } +function deleteGroupInGroupsByNames(groups: {}, groupName: string) { + let newGroups = {}; + for (let groupname in groups) { + if (groupname !== groupName) newGroups[groupname] = groups[groupname]; + } + return newGroups; +} + +function deleteGroupInEntries(groups: [], groupName: string) { + let newGroups = []; + for (let group of groups) { + if (group !== groupName) newGroups.push(group); + } + return newGroups; +} + const reducerByName = (state: any, groupname: string, newGroupState: any) => { const newGroupsByNames = { ...state, @@ -211,7 +275,16 @@ function listReducer(state: any = {}, action: any = {}) { _links: action.payload._links } }; - + // Delete single group actions + case DELETE_GROUP_SUCCESS: + const newGroupEntries = deleteGroupInEntries( + state.entries, + action.payload.name + ); + return { + ...state, + entries: newGroupEntries + }; default: return state; } @@ -229,6 +302,13 @@ function byNamesReducer(state: any = {}, action: any = {}) { }; case FETCH_GROUP_SUCCESS: return reducerByName(state, action.payload.name, action.payload); + case DELETE_GROUP_SUCCESS: + const newGroupByNames = deleteGroupInGroupsByNames( + state, + action.payload.name + ); + return newGroupByNames; + default: return state; } @@ -311,3 +391,11 @@ export function isFetchGroupPending(state: Object, name: string) { export function getFetchGroupFailure(state: Object, name: string) { return getFailure(state, FETCH_GROUP, name); } + +export function isDeleteGroupPending(state: Object, name: string) { + return isPending(state, DELETE_GROUP, name); +} + +export function getDeleteGroupFailure(state: Object, name: string) { + return getFailure(state, DELETE_GROUP, name); +} diff --git a/scm-ui/src/groups/modules/groups.test.js b/scm-ui/src/groups/modules/groups.test.js index cf3e7c0c95..1b7c0fec0b 100644 --- a/scm-ui/src/groups/modules/groups.test.js +++ b/scm-ui/src/groups/modules/groups.test.js @@ -30,7 +30,15 @@ import reducer, { CREATE_GROUP_FAILURE, isCreateGroupPending, CREATE_GROUP, - getCreateGroupFailure + getCreateGroupFailure, + deleteGroup, + DELETE_GROUP_PENDING, + DELETE_GROUP_SUCCESS, + DELETE_GROUP_FAILURE, + DELETE_GROUP, + deleteGroupSuccess, + isDeleteGroupPending, + getDeleteGroupFailure } from "./groups"; const GROUPS_URL = "/scm/api/rest/v2/groups"; @@ -45,13 +53,13 @@ const humanGroup = { members: ["userZaphod"], _links: { self: { - href: "http://localhost:3000/scm/api/rest/v2/groups/humanGroup" + href: "http://localhost:8081/scm/api/rest/v2/groups/humanGroup" }, delete: { - href: "http://localhost:3000/scm/api/rest/v2/groups/humanGroup" + href: "http://localhost:8081/scm/api/rest/v2/groups/humanGroup" }, update: { - href:"http://localhost:3000/scm/api/rest/v2/groups/humanGroup" + href:"http://localhost:8081/scm/api/rest/v2/groups/humanGroup" } }, _embedded: { @@ -60,7 +68,7 @@ const humanGroup = { name: "userZaphod", _links: { self: { - href: "http://localhost:3000/scm/api/rest/v2/users/userZaphod" + href: "http://localhost:8081/scm/api/rest/v2/users/userZaphod" } } } @@ -77,13 +85,13 @@ const emptyGroup = { members: [], _links: { self: { - href: "http://localhost:3000/scm/api/rest/v2/groups/emptyGroup" + href: "http://localhost:8081/scm/api/rest/v2/groups/emptyGroup" }, delete: { - href: "http://localhost:3000/scm/api/rest/v2/groups/emptyGroup" + href: "http://localhost:8081/scm/api/rest/v2/groups/emptyGroup" }, update: { - href:"http://localhost:3000/scm/api/rest/v2/groups/emptyGroup" + href:"http://localhost:8081/scm/api/rest/v2/groups/emptyGroup" } }, _embedded: { @@ -158,10 +166,10 @@ describe("groups fetch()", () => { }); it("should sucessfully fetch single group", () => { - fetchMock.getOnce(GROUPS_URL + "/humandGroup", humanGroup); + fetchMock.getOnce(GROUPS_URL + "/humanGroup", humanGroup); const store = mockStore({}); - return store.dispatch(fetchGroup("humandGroup")).then(() => { + return store.dispatch(fetchGroup("humanGroup")).then(() => { const actions = store.getActions(); expect(actions[0].type).toEqual(FETCH_GROUP_PENDING); expect(actions[1].type).toEqual(FETCH_GROUP_SUCCESS); @@ -170,12 +178,12 @@ describe("groups fetch()", () => { }); it("should fail fetching single group on HTTP 500", () => { - fetchMock.getOnce(GROUPS_URL + "/humandGroup", { + fetchMock.getOnce(GROUPS_URL + "/humanGroup", { status: 500 }); const store = mockStore({}); - return store.dispatch(fetchGroup("humandGroup")).then(() => { + return store.dispatch(fetchGroup("humanGroup")).then(() => { const actions = store.getActions(); expect(actions[0].type).toEqual(FETCH_GROUP_PENDING); expect(actions[1].type).toEqual(FETCH_GROUP_FAILURE); @@ -211,6 +219,53 @@ describe("groups fetch()", () => { expect(actions[1].payload instanceof Error).toBeTruthy(); }); }); + + it("should delete successfully group humanGroup", () => { + fetchMock.deleteOnce("http://localhost:8081/scm/api/rest/v2/groups/humanGroup", { + status: 204 + }); + + const store = mockStore({}); + return store.dispatch(deleteGroup(humanGroup)).then(() => { + const actions = store.getActions(); + expect(actions.length).toBe(2); + expect(actions[0].type).toEqual(DELETE_GROUP_PENDING); + expect(actions[0].payload).toBe(humanGroup); + expect(actions[1].type).toEqual(DELETE_GROUP_SUCCESS); + }); + }); + + it("should call the callback, after successful delete", () => { + fetchMock.deleteOnce("http://localhost:8081/scm/api/rest/v2/groups/humanGroup", { + status: 204 + }); + + let called = false; + const callMe = () => { + called = true; + }; + + const store = mockStore({}); + return store.dispatch(deleteGroup(humanGroup, callMe)).then(() => { + expect(called).toBeTruthy(); + }); + }); + + it("should fail to delete group humanGroup", () => { + fetchMock.deleteOnce("http://localhost:8081/scm/api/rest/v2/groups/humanGroup", { + status: 500 + }); + + const store = mockStore({}); + return store.dispatch(deleteGroup(humanGroup)).then(() => { + const actions = store.getActions(); + expect(actions[0].type).toEqual(DELETE_GROUP_PENDING); + expect(actions[0].payload).toBe(humanGroup); + expect(actions[1].type).toEqual(DELETE_GROUP_FAILURE); + expect(actions[1].payload).toBeDefined(); + }); + }); + }); describe("groups reducer", () => { @@ -283,6 +338,24 @@ describe("groups reducer", () => { expect(newState.list.entries).toEqual(["humanGroup"]); }); + it("should remove group from state when delete succeeds", () => { + const state = { + list: { + entries: ["humanGroup", "emptyGroup"] + }, + byNames: { + humanGroup: humanGroup, + emptyGroup: emptyGroup + } + }; + + const newState = reducer(state, deleteGroupSuccess(emptyGroup)); + expect(newState.byNames["humanGroup"]).toBeDefined(); + expect(newState.byNames["emptyGroup"]).toBeFalsy(); + expect(newState.list.entries).toEqual(["humanGroup"]); + }); + + }); describe("selector tests", () => { @@ -384,30 +457,30 @@ describe("selector tests", () => { expect(getGroupByName(state, "emptyGroup")).toEqual(emptyGroup); }); - it("should return true, when fetch group humandGroup is pending", () => { + it("should return true, when fetch group humanGroup is pending", () => { const state = { pending: { - [FETCH_GROUP + "/humandGroup"]: true + [FETCH_GROUP + "/humanGroup"]: true } }; - expect(isFetchGroupPending(state, "humandGroup")).toEqual(true); + expect(isFetchGroupPending(state, "humanGroup")).toEqual(true); }); - it("should return false, when fetch group humandGroup is not pending", () => { - expect(isFetchGroupPending({}, "humandGroup")).toEqual(false); + it("should return false, when fetch group humanGroup is not pending", () => { + expect(isFetchGroupPending({}, "humanGroup")).toEqual(false); }); - it("should return error when fetch group humandGroup did fail", () => { + it("should return error when fetch group humanGroup did fail", () => { const state = { failure: { - [FETCH_GROUP + "/humandGroup"]: error + [FETCH_GROUP + "/humanGroup"]: error } }; - expect(getFetchGroupFailure(state, "humandGroup")).toEqual(error); + expect(getFetchGroupFailure(state, "humanGroup")).toEqual(error); }); - it("should return undefined when fetch group humandGroup did not fail", () => { - expect(getFetchGroupFailure({}, "humandGroup")).toBe(undefined); + it("should return undefined when fetch group humanGroup did not fail", () => { + expect(getFetchGroupFailure({}, "humanGroup")).toBe(undefined); }); it("should return true if create group is pending", () => { @@ -432,4 +505,31 @@ describe("selector tests", () => { expect(getCreateGroupFailure({})).toBeUndefined() }) + + it("should return true, when delete group humanGroup is pending", () => { + const state = { + pending: { + [DELETE_GROUP + "/humanGroup"]: true + } + }; + expect(isDeleteGroupPending(state, "humanGroup")).toEqual(true); + }); + + it("should return false, when delete group humanGroup is not pending", () => { + expect(isDeleteGroupPending({}, "humanGroup")).toEqual(false); + }); + + it("should return error when delete group humanGroup did fail", () => { + const state = { + failure: { + [DELETE_GROUP + "/humanGroup"]: error + } + }; + expect(getDeleteGroupFailure(state, "humanGroup")).toEqual(error); + }); + + it("should return undefined when delete group humanGroup did not fail", () => { + expect(getDeleteGroupFailure({}, "humanGroup")).toBe(undefined); + }); + });