diff --git a/scm-ui-components/packages/ui-types/src/Role.js b/scm-ui-components/packages/ui-types/src/Role.js index 385b498d38..00e794b886 100644 --- a/scm-ui-components/packages/ui-types/src/Role.js +++ b/scm-ui-components/packages/ui-types/src/Role.js @@ -1,9 +1,12 @@ //@flow +import type { Links } from "./hal"; + export type Role = { name: string, - creationDate: number | null, - lastModified: number | null, - type: string, - verb: string[] + verbs: string[], + creationDate?: number, + lastModified?: number, + system: boolean, + _links: Links }; diff --git a/scm-ui/src/config/modules/roles.js b/scm-ui/src/config/modules/roles.js index 823fbb7aa7..af81190bb4 100644 --- a/scm-ui/src/config/modules/roles.js +++ b/scm-ui/src/config/modules/roles.js @@ -4,7 +4,7 @@ import { isPending } from "../../modules/pending"; import { getFailure } from "../../modules/failure"; import * as types from "../../modules/types"; import { combineReducers, Dispatch } from "redux"; -import type {Action, PagedCollection, Role} from "@scm-manager/ui-types"; +import type { Action, PagedCollection, Role } from "@scm-manager/ui-types"; export const FETCH_ROLES = "scm/roles/FETCH_ROLES"; export const FETCH_ROLES_PENDING = `${FETCH_ROLES}_${types.PENDING_SUFFIX}`; @@ -318,24 +318,22 @@ function deleteRoleInEntries(roles: [], roleName: string) { } const reducerByName = (state: any, rolename: string, newRoleState: any) => { - const newRolesByNames = { + return { ...state, [rolename]: newRoleState }; - - return newRolesByNames; }; function listReducer(state: any = {}, action: any = {}) { switch (action.type) { case FETCH_ROLES_SUCCESS: - const roles = action.payload._embedded.roles; + const roles = action.payload._embedded.repositoryRoles; const roleNames = roles.map(role => role.name); return { ...state, entries: roleNames, entry: { - roleCreatePermission: action.payload._links.create ? true : false, + roleCreatePermission: !!action.payload._links.create, page: action.payload.page, pageTotal: action.payload.pageTotal, _links: action.payload._links @@ -361,7 +359,7 @@ function byNamesReducer(state: any = {}, action: any = {}) { switch (action.type) { // Fetch all roles actions case FETCH_ROLES_SUCCESS: - const roles = action.payload._embedded.roles; + const roles = action.payload._embedded.repositoryRoles; const roleNames = roles.map(role => role.name); const byNames = extractRolesByNames(roles, roleNames, state.byNames); return { @@ -373,10 +371,7 @@ function byNamesReducer(state: any = {}, action: any = {}) { return reducerByName(state, action.payload.name, action.payload); case DELETE_ROLE_SUCCESS: - return deleteRoleInRolesByNames( - state, - action.payload.name - ); + return deleteRoleInRolesByNames(state, action.payload.name); default: return state; @@ -390,8 +385,8 @@ export default combineReducers({ // selectors const selectList = (state: Object) => { - if (state.roles && state.roles.list) { - return state.roles.list; + if (state.repositoryRoles && state.repositoryRoles.list) { + return state.repositoryRoles.list; } return {}; }; @@ -409,11 +404,7 @@ export const selectListAsCollection = (state: Object): PagedCollection => { }; export const isPermittedToCreateRoles = (state: Object): boolean => { - const permission = selectListEntry(state).roleCreatePermission; - if (permission) { - return true; - } - return false; + return selectListEntry(state).roleCreatePermission; }; export function getRolesFromState(state: Object) { @@ -424,7 +415,7 @@ export function getRolesFromState(state: Object) { const roleEntries: Role[] = []; for (let roleName of roleNames) { - roleEntries.push(state.roles.byNames[roleName]); + roleEntries.push(state.repositoryRoles.byNames[roleName]); } return roleEntries; @@ -447,8 +438,8 @@ export function getCreateRoleFailure(state: Object) { } export function getRoleByName(state: Object, name: string) { - if (state.roles && state.roles.byNames) { - return state.roles.byNames[name]; + if (state.repositoryRoles && state.repositoryRoles.byNames) { + return state.repositoryRoles.byNames[name]; } } diff --git a/scm-ui/src/config/modules/roles.test.js b/scm-ui/src/config/modules/roles.test.js index 7bb6bdcc60..2d2e30012c 100644 --- a/scm-ui/src/config/modules/roles.test.js +++ b/scm-ui/src/config/modules/roles.test.js @@ -49,11 +49,6 @@ import reducer, { isPermittedToCreateRoles } from "./roles"; -const URL = "roles"; -const ROLES_URL = "api/v2/repositoryPermissions"; - -const error = new Error("FEHLER!"); - const verbs = [ "createPullRequest", "readPullRequest", @@ -72,49 +67,64 @@ const verbs = [ "*" ]; -const repositoryRoles = { - availableVerbs: verbs, - availableRoles: [ - { - name: "READ", - creationDate: null, - lastModified: null, - type: "system", - verb: ["read", "pull", "readPullRequest"] - }, - { - name: "WRITE", - creationDate: null, - lastModified: null, - type: "system", - verb: [ - "read", - "pull", - "push", - "createPullRequest", - "readPullRequest", - "commentPullRequest", - "mergePullRequest" - ] - }, - { - name: "OWNER", - creationDate: null, - lastModified: null, - type: "system", - verb: ["*"] - } - ], +const role1 = { + name: "SPECIALROLE", + verbs: ["read", "pull", "push", "readPullRequest"], + system: false, _links: { self: { - href: "http://localhost:8081/scm/api/v2/repositoryPermissions/" + href: "http://localhost:8081/scm/api/v2/repositoryRoles/SPECIALROLE" + }, + delete: { + href: "http://localhost:8081/scm/api/v2/repositoryRoles/SPECIALROLE" + }, + update: { + href: "http://localhost:8081/scm/api/v2/repositoryRoles/SPECIALROLE" + } + } +}; +const role2 = { + name: "WRITE", + verbs: [ + "read", + "pull", + "push", + "createPullRequest", + "readPullRequest", + "commentPullRequest", + "mergePullRequest" + ], + system: true, + _links: { + self: { + href: "http://localhost:8081/scm/api/v2/repositoryRoles/WRITE" } } }; const responseBody = { - entries: repositoryRoles, - rolesUpdatePermission: false + page: 0, + pageTotal: 1, + _links: { + self: { + href: + "http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10" + }, + first: { + href: + "http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10" + }, + last: { + href: + "http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10" + }, + create: { + href: "http://localhost:8081/scm/api/v2/repositoryRoles/" + } + }, + _embedded: { + repositoryRoles: [role1, role2] + } }; const response = { @@ -122,6 +132,12 @@ const response = { responseBody }; +const URL = "repositoryRoles"; +const ROLES_URL = "/api/v2/repositoryRoles"; +const ROLE1_URL = "http://localhost:8081/api/v2/repositoryRoles/SPECIALROLE"; + +const error = new Error("FEHLER!"); + describe("repository roles fetch()", () => { const mockStore = configureMockStore([thunk]); afterEach(() => { @@ -246,10 +262,6 @@ describe("repository roles fetch()", () => { }); }); - - - - it("should fail updating role on HTTP 500", () => { fetchMock.putOnce(ROLE1_URL, { status: 500 @@ -311,40 +323,95 @@ describe("repository roles fetch()", () => { }); }); -describe("repository roles reducer", () => { +describe("roles reducer", () => { it("should update state correctly according to FETCH_ROLES_SUCCESS action", () => { + const newState = reducer({}, fetchRolesSuccess(responseBody)); + expect(newState.list).toEqual({ + entries: ["SPECIALROLE", "WRITE"], + entry: { + roleCreatePermission: true, + page: 0, + pageTotal: 1, + _links: responseBody._links + } + }); + + expect(newState.byNames).toEqual({ + SPECIALROLE: role1, + WRITE: role2 + }); + + expect(newState.list.entry.roleCreatePermission).toBeTruthy(); }); it("should set roleCreatePermission to true if update link is present", () => { + const newState = reducer({}, fetchRolesSuccess(responseBody)); + expect(newState.list.entry.roleCreatePermission).toBeTruthy(); }); it("should not replace whole byNames map when fetching roles", () => { + const oldState = { + byNames: { + WRITE: role2 + } + }; + const newState = reducer(oldState, fetchRolesSuccess(responseBody)); + expect(newState.byNames["SPECIALROLE"]).toBeDefined(); + expect(newState.byNames["WRITE"]).toBeDefined(); }); it("should remove role from state when delete succeeds", () => { + const state = { + list: { + entries: ["WRITE", "SPECIALROLE"] + }, + byNames: { + SPECIALROLE: role1, + WRITE: role2 + } + }; + const newState = reducer(state, deleteRoleSuccess(role2)); + expect(newState.byNames["SPECIALROLE"]).toBeDefined(); + expect(newState.byNames["WRITE"]).toBeFalsy(); + expect(newState.list.entries).toEqual(["SPECIALROLE"]); }); it("should set roleCreatePermission to true if create link is present", () => { + const newState = reducer({}, fetchRolesSuccess(responseBody)); + expect(newState.list.entry.roleCreatePermission).toBeTruthy(); + expect(newState.list.entries).toEqual(["SPECIALROLE", "WRITE"]); + expect(newState.byNames["WRITE"]).toBeTruthy(); + expect(newState.byNames["SPECIALROLE"]).toBeTruthy(); }); it("should update state according to FETCH_ROLE_SUCCESS action", () => { - + const newState = reducer({}, fetchRoleSuccess(role2)); + expect(newState.byNames["WRITE"]).toBe(role2); }); it("should affect roles state nor the state of other roles", () => { - + const newState = reducer( + { + list: { + entries: ["SPECIALROLE"] + } + }, + fetchRoleSuccess(role2) + ); + expect(newState.byNames["WRITE"]).toBe(role2); + expect(newState.list.entries).toEqual(["SPECIALROLE"]); }); }); describe("selector tests", () => { it("should return an empty object", () => { expect(selectListAsCollection({})).toEqual({}); - expect(selectListAsCollection({ roles: { a: "a" } })).toEqual({}); + expect(selectListAsCollection({ repositoryRoles: { a: "a" } })).toEqual({}); }); it("should return a state slice collection", () => { @@ -354,7 +421,7 @@ describe("selector tests", () => { }; const state = { - roles: { + repositoryRoles: { list: { entry: collection } @@ -365,19 +432,19 @@ describe("selector tests", () => { it("should return false", () => { expect(isPermittedToCreateRoles({})).toBe(false); - expect(isPermittedToCreateRoles({ roles: { list: { entry: {} } } })).toBe( + expect(isPermittedToCreateRoles({ repositoryRoles: { list: { entry: {} } } })).toBe( false ); expect( isPermittedToCreateRoles({ - roles: { list: { entry: { roleCreatePermission: false } } } + repositoryRoles: { list: { entry: { roleCreatePermission: false } } } }) ).toBe(false); }); it("should return true", () => { const state = { - roles: { + repositoryRoles: { list: { entry: { roleCreatePermission: true @@ -388,9 +455,9 @@ describe("selector tests", () => { expect(isPermittedToCreateRoles(state)).toBe(true); }); - it("should get roles from state", () => { + it("should get repositoryRoles from state", () => { const state = { - roles: { + repositoryRoles: { list: { entries: ["a", "b"] }, @@ -403,7 +470,7 @@ describe("selector tests", () => { expect(getRolesFromState(state)).toEqual([{ name: "a" }, { name: "b" }]); }); - it("should return true, when fetch roles is pending", () => { + it("should return true, when fetch repositoryRoles is pending", () => { const state = { pending: { [FETCH_ROLES]: true @@ -412,11 +479,11 @@ describe("selector tests", () => { expect(isFetchRolesPending(state)).toEqual(true); }); - it("should return false, when fetch roles is not pending", () => { + it("should return false, when fetch repositoryRoles is not pending", () => { expect(isFetchRolesPending({})).toEqual(false); }); - it("should return error when fetch roles did fail", () => { + it("should return error when fetch repositoryRoles did fail", () => { const state = { failure: { [FETCH_ROLES]: error @@ -425,7 +492,7 @@ describe("selector tests", () => { expect(getFetchRolesFailure(state)).toEqual(error); }); - it("should return undefined when fetch roles did not fail", () => { + it("should return undefined when fetch repositoryRoles did not fail", () => { expect(getFetchRolesFailure({})).toBe(undefined); }); @@ -462,7 +529,7 @@ describe("selector tests", () => { it("should return role1", () => { const state = { - roles: { + repositoryRoles: { byNames: { role1: role1 } @@ -549,4 +616,3 @@ describe("selector tests", () => { expect(getDeleteRoleFailure({}, "role2")).toBe(undefined); }); }); -