Merge with upstream

This commit is contained in:
René Pfeuffer
2019-05-08 11:01:52 +02:00
11 changed files with 1255 additions and 37 deletions

View File

@@ -0,0 +1,9 @@
//@flow
export type Role = {
name: string,
creationDate: number | null,
lastModified: number | null,
type: string,
verb: string[]
};

View File

@@ -16,6 +16,7 @@ export type { Changeset } from "./Changesets";
export type { Tag } from "./Tags";
export type { Config } from "./Config";
export type { Role } from "./Role";
export type { IndexResources } from "./IndexResources";

View File

@@ -0,0 +1,34 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import type { Role } from "@scm-manager/ui-types";
type Props = {
t: string => string,
roles: Role[]
};
class RoleTable extends React.Component<Props> {
render() {
const { roles, t } = this.props;
return (
<table className="card-table table is-hoverable is-fullwidth">
<thead>
<tr>
<th className="is-hidden-mobile">{t("user.name")}</th>
<th>{t("user.displayName")}</th>
<th>{t("user.mail")}</th>
<th className="is-hidden-mobile">{t("user.active")}</th>
</tr>
</thead>
<tbody>
{roles.map((role, index) => {
return <p key={index}>role</p>;
})}
</tbody>
</table>
);
}
}
export default translate("config")(RoleTable);

View File

@@ -7,6 +7,7 @@ import { ExtensionPoint } from "@scm-manager/ui-extensions";
import type { Links } from "@scm-manager/ui-types";
import { Page, Navigation, NavLink, Section } from "@scm-manager/ui-components";
import GlobalConfig from "./GlobalConfig";
import GlobalPermissionRoles from "./GlobalPermissionRoles";
import type { History } from "history";
import { connect } from "react-redux";
import { compose } from "redux";
@@ -47,6 +48,11 @@ class Config extends React.Component<Props> {
<div className="columns">
<div className="column is-three-quarters">
<Route path={url} exact component={GlobalConfig} />
<Route
path={`${url}/roles`}
exact
component={GlobalPermissionRoles}
/>
<ExtensionPoint
name="config.route"
props={extensionProps}
@@ -60,6 +66,10 @@ class Config extends React.Component<Props> {
to={`${url}`}
label={t("config.globalConfigurationNavLink")}
/>
<NavLink
to={`${url}/roles`}
label={t("config.roles.navLink")}
/>
<ExtensionPoint
name="config.navigation"
props={extensionProps}

View File

@@ -0,0 +1,130 @@
// @flow
import React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import type { History } from "history";
import type {Role, PagedCollection} from "@scm-manager/ui-types";
import {
fetchRolesByPage,
getRolesFromState,
selectListAsCollection,
isPermittedToCreateRoles,
isFetchRolesPending,
getFetchRolesFailure
} from "../modules/roles";
import {
Title,
Loading,
Notification,
LinkPaginator,
urls,
CreateButton
} from "@scm-manager/ui-components";
import RoleTable from "../components/table/RoleTable";
import { getRolesLink } from "../../modules/indexResource";
type Props = {
roles: Role[],
loading: boolean,
error: Error,
canAddRoles: boolean,
list: PagedCollection,
page: number,
rolesLink: string,
// context objects
t: string => string,
history: History,
location: any,
// dispatch functions
fetchRolesByPage: (link: string, page: number, filter?: string) => void
};
class GlobalPermissionRoles extends React.Component<Props> {
componentDidMount() {
const { fetchRolesByPage, rolesLink, page, location } = this.props;
fetchRolesByPage(
rolesLink,
page,
urls.getQueryStringFromLocation(location)
);
}
render() {
const { t, loading } = this.props;
if (loading) {
return <Loading />;
}
return (
<div>
<Title title={t("config.roles.title")} />
{this.renderPermissionsTable()}
{this.renderCreateButton()}
</div>
);
}
renderPermissionsTable() {
const { roles, list, page, location, t } = this.props;
if (roles && roles.length > 0) {
return (
<>
<RoleTable roles={roles} />
<LinkPaginator
collection={list}
page={page}
filter={urls.getQueryStringFromLocation(location)}
/>
</>
);
}
return <Notification type="info">{t("config.roles.noPermissionRoles")}</Notification>;
}
renderCreateButton() {
const { canAddRoles, t } = this.props;
if (canAddRoles) {
return (
<CreateButton label={t("config.permissions.createButton")} link="/create" />
);
}
return null;
}
}
const mapStateToProps = (state, ownProps) => {
const { match } = ownProps;
const roles = getRolesFromState(state);
const loading = isFetchRolesPending(state);
const error = getFetchRolesFailure(state);
const page = urls.getPageFromMatch(match);
const canAddRoles = isPermittedToCreateRoles(state);
const list = selectListAsCollection(state);
const rolesLink = getRolesLink(state);
return {
roles,
loading,
error,
canAddRoles,
list,
page,
rolesLink
};
};
const mapDispatchToProps = dispatch => {
return {
fetchRolesByPage: (link: string, page: number, filter?: string) => {
dispatch(fetchRolesByPage(link, page, filter));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(translate("config")(GlobalPermissionRoles));

View File

@@ -0,0 +1,477 @@
// @flow
import { apiClient } from "@scm-manager/ui-components";
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";
export const FETCH_ROLES = "scm/roles/FETCH_ROLES";
export const FETCH_ROLES_PENDING = `${FETCH_ROLES}_${types.PENDING_SUFFIX}`;
export const FETCH_ROLES_SUCCESS = `${FETCH_ROLES}_${types.SUCCESS_SUFFIX}`;
export const FETCH_ROLES_FAILURE = `${FETCH_ROLES}_${types.FAILURE_SUFFIX}`;
export const FETCH_ROLE = "scm/roles/FETCH_ROLE";
export const FETCH_ROLE_PENDING = `${FETCH_ROLE}_${types.PENDING_SUFFIX}`;
export const FETCH_ROLE_SUCCESS = `${FETCH_ROLE}_${types.SUCCESS_SUFFIX}`;
export const FETCH_ROLE_FAILURE = `${FETCH_ROLE}_${types.FAILURE_SUFFIX}`;
export const CREATE_ROLE = "scm/roles/CREATE_ROLE";
export const CREATE_ROLE_PENDING = `${CREATE_ROLE}_${types.PENDING_SUFFIX}`;
export const CREATE_ROLE_SUCCESS = `${CREATE_ROLE}_${types.SUCCESS_SUFFIX}`;
export const CREATE_ROLE_FAILURE = `${CREATE_ROLE}_${types.FAILURE_SUFFIX}`;
export const CREATE_ROLE_RESET = `${CREATE_ROLE}_${types.RESET_SUFFIX}`;
export const MODIFY_ROLE = "scm/roles/MODIFY_ROLE";
export const MODIFY_ROLE_PENDING = `${MODIFY_ROLE}_${types.PENDING_SUFFIX}`;
export const MODIFY_ROLE_SUCCESS = `${MODIFY_ROLE}_${types.SUCCESS_SUFFIX}`;
export const MODIFY_ROLE_FAILURE = `${MODIFY_ROLE}_${types.FAILURE_SUFFIX}`;
export const MODIFY_ROLE_RESET = `${MODIFY_ROLE}_${types.RESET_SUFFIX}`;
export const DELETE_ROLE = "scm/roles/DELETE_ROLE";
export const DELETE_ROLE_PENDING = `${DELETE_ROLE}_${types.PENDING_SUFFIX}`;
export const DELETE_ROLE_SUCCESS = `${DELETE_ROLE}_${types.SUCCESS_SUFFIX}`;
export const DELETE_ROLE_FAILURE = `${DELETE_ROLE}_${types.FAILURE_SUFFIX}`;
const CONTENT_TYPE_ROLE = "application/vnd.scmm-role+json;v=2";
// fetch roles
export function fetchRolesPending(): Action {
return {
type: FETCH_ROLES_PENDING
};
}
export function fetchRolesSuccess(roles: any): Action {
return {
type: FETCH_ROLES_SUCCESS,
payload: roles
};
}
export function fetchRolesFailure(url: string, error: Error): Action {
return {
type: FETCH_ROLES_FAILURE,
payload: {
error,
url
}
};
}
export function fetchRolesByLink(link: string) {
return function(dispatch: any) {
dispatch(fetchRolesPending());
return apiClient
.get(link)
.then(response => response.json())
.then(data => {
dispatch(fetchRolesSuccess(data));
})
.catch(error => {
dispatch(fetchRolesFailure(link, error));
});
};
}
export function fetchRoles(link: string) {
return fetchRolesByLink(link);
}
export function fetchRolesByPage(link: string, page: number, filter?: string) {
// backend start counting by 0
if (filter) {
return fetchRolesByLink(
`${link}?page=${page - 1}&q=${decodeURIComponent(filter)}`
);
}
return fetchRolesByLink(`${link}?page=${page - 1}`);
}
// fetch role
export function fetchRolePending(name: string): Action {
return {
type: FETCH_ROLE_PENDING,
payload: name,
itemId: name
};
}
export function fetchRoleSuccess(role: any): Action {
return {
type: FETCH_ROLE_SUCCESS,
payload: role,
itemId: role.name
};
}
export function fetchRoleFailure(name: string, error: Error): Action {
return {
type: FETCH_ROLE_FAILURE,
payload: {
name,
error
},
itemId: name
};
}
function fetchRole(link: string, name: string) {
return function(dispatch: any) {
dispatch(fetchRolePending(name));
return apiClient
.get(link)
.then(response => {
return response.json();
})
.then(data => {
dispatch(fetchRoleSuccess(data));
})
.catch(error => {
dispatch(fetchRoleFailure(name, error));
});
};
}
export function fetchRoleByName(link: string, name: string) {
const roleUrl = link.endsWith("/") ? link + name : link + "/" + name;
return fetchRole(roleUrl, name);
}
export function fetchRoleByLink(role: Role) {
return fetchRole(role._links.self.href, role.name);
}
// create role
export function createRolePending(role: Role): Action {
return {
type: CREATE_ROLE_PENDING,
role
};
}
export function createRoleSuccess(): Action {
return {
type: CREATE_ROLE_SUCCESS
};
}
export function createRoleFailure(error: Error): Action {
return {
type: CREATE_ROLE_FAILURE,
payload: error
};
}
export function createRoleReset() {
return {
type: CREATE_ROLE_RESET
};
}
export function createRole(link: string, role: Role, callback?: () => void) {
return function(dispatch: Dispatch) {
dispatch(createRolePending(role));
return apiClient
.post(link, role, CONTENT_TYPE_ROLE)
.then(() => {
dispatch(createRoleSuccess());
if (callback) {
callback();
}
})
.catch(error => dispatch(createRoleFailure(error)));
};
}
// modify group
export function modifyRolePending(role: Role): Action {
return {
type: MODIFY_ROLE_PENDING,
payload: role,
itemId: role.name
};
}
export function modifyRoleSuccess(role: Role): Action {
return {
type: MODIFY_ROLE_SUCCESS,
payload: role,
itemId: role.name
};
}
export function modifyRoleFailure(role: Role, error: Error): Action {
return {
type: MODIFY_ROLE_FAILURE,
payload: {
error,
role
},
itemId: role.name
};
}
export function modifyRoleReset(role: Role): Action {
return {
type: MODIFY_ROLE_RESET,
itemId: role.name
};
}
export function modifyRole(role: Role, callback?: () => void) {
return function(dispatch: Dispatch) {
dispatch(modifyRolePending(role));
return apiClient
.put(role._links.update.href, role, CONTENT_TYPE_ROLE)
.then(() => {
dispatch(modifyRoleSuccess(role));
if (callback) {
callback();
}
})
.then(() => {
dispatch(fetchRoleByLink(role));
})
.catch(err => {
dispatch(modifyRoleFailure(role, err));
});
};
}
// delete role
export function deleteRolePending(role: Role): Action {
return {
type: DELETE_ROLE_PENDING,
payload: role,
itemId: role.name
};
}
export function deleteRoleSuccess(role: Role): Action {
return {
type: DELETE_ROLE_SUCCESS,
payload: role,
itemId: role.name
};
}
export function deleteRoleFailure(role: Role, error: Error): Action {
return {
type: DELETE_ROLE_FAILURE,
payload: {
error,
role
},
itemId: role.name
};
}
export function deleteRole(role: Role, callback?: () => void) {
return function(dispatch: any) {
dispatch(deleteRolePending(role));
return apiClient
.delete(role._links.delete.href)
.then(() => {
dispatch(deleteRoleSuccess(role));
if (callback) {
callback();
}
})
.catch(error => {
dispatch(deleteRoleFailure(role, error));
});
};
}
function extractRolesByNames(
roles: Role[],
roleNames: string[],
oldRolesByNames: Object
) {
const rolesByNames = {};
for (let role of roles) {
rolesByNames[role.name] = role;
}
for (let roleName in oldRolesByNames) {
rolesByNames[roleName] = oldRolesByNames[roleName];
}
return rolesByNames;
}
function deleteRoleInRolesByNames(roles: {}, roleName: string) {
let newRoles = {};
for (let rolename in roles) {
if (rolename !== roleName) newRoles[rolename] = roles[rolename];
}
return newRoles;
}
function deleteRoleInEntries(roles: [], roleName: string) {
let newRoles = [];
for (let role of roles) {
if (role !== roleName) newRoles.push(role);
}
return newRoles;
}
const reducerByName = (state: any, rolename: string, newRoleState: any) => {
const newRolesByNames = {
...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 roleNames = roles.map(role => role.name);
return {
...state,
entries: roleNames,
entry: {
roleCreatePermission: action.payload._links.create ? true : false,
page: action.payload.page,
pageTotal: action.payload.pageTotal,
_links: action.payload._links
}
};
// Delete single role actions
case DELETE_ROLE_SUCCESS:
const newRoleEntries = deleteRoleInEntries(
state.entries,
action.payload.name
);
return {
...state,
entries: newRoleEntries
};
default:
return state;
}
}
function byNamesReducer(state: any = {}, action: any = {}) {
switch (action.type) {
// Fetch all roles actions
case FETCH_ROLES_SUCCESS:
const roles = action.payload._embedded.roles;
const roleNames = roles.map(role => role.name);
const byNames = extractRolesByNames(roles, roleNames, state.byNames);
return {
...byNames
};
// Fetch single role actions
case FETCH_ROLE_SUCCESS:
return reducerByName(state, action.payload.name, action.payload);
case DELETE_ROLE_SUCCESS:
return deleteRoleInRolesByNames(
state,
action.payload.name
);
default:
return state;
}
}
export default combineReducers({
list: listReducer,
byNames: byNamesReducer
});
// selectors
const selectList = (state: Object) => {
if (state.roles && state.roles.list) {
return state.roles.list;
}
return {};
};
const selectListEntry = (state: Object): Object => {
const list = selectList(state);
if (list.entry) {
return list.entry;
}
return {};
};
export const selectListAsCollection = (state: Object): PagedCollection => {
return selectListEntry(state);
};
export const isPermittedToCreateRoles = (state: Object): boolean => {
const permission = selectListEntry(state).roleCreatePermission;
if (permission) {
return true;
}
return false;
};
export function getRolesFromState(state: Object) {
const roleNames = selectList(state).entries;
if (!roleNames) {
return null;
}
const roleEntries: Role[] = [];
for (let roleName of roleNames) {
roleEntries.push(state.roles.byNames[roleName]);
}
return roleEntries;
}
export function isFetchRolesPending(state: Object) {
return isPending(state, FETCH_ROLES);
}
export function getFetchRolesFailure(state: Object) {
return getFailure(state, FETCH_ROLES);
}
export function isCreateRolePending(state: Object) {
return isPending(state, CREATE_ROLE);
}
export function getCreateRoleFailure(state: Object) {
return getFailure(state, CREATE_ROLE);
}
export function getRoleByName(state: Object, name: string) {
if (state.roles && state.roles.byNames) {
return state.roles.byNames[name];
}
}
export function isFetchRolePending(state: Object, name: string) {
return isPending(state, FETCH_ROLE, name);
}
export function getFetchRoleFailure(state: Object, name: string) {
return getFailure(state, FETCH_ROLE, name);
}
export function isModifyRolePending(state: Object, name: string) {
return isPending(state, MODIFY_ROLE, name);
}
export function getModifyRoleFailure(state: Object, name: string) {
return getFailure(state, MODIFY_ROLE, name);
}
export function isDeleteRolePending(state: Object, name: string) {
return isPending(state, DELETE_ROLE, name);
}
export function getDeleteRoleFailure(state: Object, name: string) {
return getFailure(state, DELETE_ROLE, name);
}

View File

@@ -0,0 +1,552 @@
// @flow
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import fetchMock from "fetch-mock";
import reducer, {
FETCH_ROLES,
FETCH_ROLES_PENDING,
FETCH_ROLES_SUCCESS,
FETCH_ROLES_FAILURE,
FETCH_ROLE,
FETCH_ROLE_PENDING,
FETCH_ROLE_SUCCESS,
FETCH_ROLE_FAILURE,
CREATE_ROLE,
CREATE_ROLE_PENDING,
CREATE_ROLE_SUCCESS,
CREATE_ROLE_FAILURE,
MODIFY_ROLE,
MODIFY_ROLE_PENDING,
MODIFY_ROLE_SUCCESS,
MODIFY_ROLE_FAILURE,
DELETE_ROLE,
DELETE_ROLE_PENDING,
DELETE_ROLE_SUCCESS,
DELETE_ROLE_FAILURE,
fetchRoles,
getFetchRolesFailure,
getRolesFromState,
isFetchRolesPending,
fetchRolesSuccess,
fetchRoleByLink,
fetchRoleByName,
fetchRoleSuccess,
isFetchRolePending,
getFetchRoleFailure,
createRole,
isCreateRolePending,
getCreateRoleFailure,
getRoleByName,
modifyRole,
isModifyRolePending,
getModifyRoleFailure,
deleteRole,
isDeleteRolePending,
deleteRoleSuccess,
getDeleteRoleFailure,
selectListAsCollection,
isPermittedToCreateRoles
} from "./roles";
const URL = "roles";
const ROLES_URL = "api/v2/repositoryPermissions";
const error = new Error("FEHLER!");
const verbs = [
"createPullRequest",
"readPullRequest",
"commentPullRequest",
"modifyPullRequest",
"mergePullRequest",
"git",
"hg",
"read",
"modify",
"delete",
"pull",
"push",
"permissionRead",
"permissionWrite",
"*"
];
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: ["*"]
}
],
_links: {
self: {
href: "http://localhost:8081/scm/api/v2/repositoryPermissions/"
}
}
};
const responseBody = {
entries: repositoryRoles,
rolesUpdatePermission: false
};
const response = {
headers: { "content-type": "application/json" },
responseBody
};
describe("repository roles fetch()", () => {
const mockStore = configureMockStore([thunk]);
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it("should successfully fetch repository roles", () => {
fetchMock.getOnce(ROLES_URL, response);
const expectedActions = [
{ type: FETCH_ROLES_PENDING },
{
type: FETCH_ROLES_SUCCESS,
payload: response
}
];
const store = mockStore({});
return store.dispatch(fetchRoles(URL)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
it("should fail getting repository roles on HTTP 500", () => {
fetchMock.getOnce(ROLES_URL, {
status: 500
});
const store = mockStore({});
return store.dispatch(fetchRoles(URL)).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_ROLES_PENDING);
expect(actions[1].type).toEqual(FETCH_ROLES_FAILURE);
expect(actions[1].payload).toBeDefined();
});
});
it("should add a role successfully", () => {
// unmatched
fetchMock.postOnce(ROLES_URL, {
status: 204
});
// after create, the roles are fetched again
fetchMock.getOnce(ROLES_URL, response);
const store = mockStore({});
return store.dispatch(createRole(URL, role1)).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(CREATE_ROLE_PENDING);
expect(actions[1].type).toEqual(CREATE_ROLE_SUCCESS);
});
});
it("should fail adding a role on HTTP 500", () => {
fetchMock.postOnce(ROLES_URL, {
status: 500
});
const store = mockStore({});
return store.dispatch(createRole(URL, role1)).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(CREATE_ROLE_PENDING);
expect(actions[1].type).toEqual(CREATE_ROLE_FAILURE);
expect(actions[1].payload).toBeDefined();
});
});
it("should call the callback after role successfully created", () => {
// unmatched
fetchMock.postOnce(ROLES_URL, {
status: 204
});
let callMe = "not yet";
const callback = () => {
callMe = "yeah";
};
const store = mockStore({});
return store.dispatch(createRole(URL, role1, callback)).then(() => {
expect(callMe).toBe("yeah");
});
});
it("successfully update role", () => {
fetchMock.putOnce(ROLE1_URL, {
status: 204
});
fetchMock.getOnce(ROLE1_URL, role1);
const store = mockStore({});
return store.dispatch(modifyRole(role1)).then(() => {
const actions = store.getActions();
expect(actions.length).toBe(3);
expect(actions[0].type).toEqual(MODIFY_ROLE_PENDING);
expect(actions[1].type).toEqual(MODIFY_ROLE_SUCCESS);
expect(actions[2].type).toEqual(FETCH_ROLE_PENDING);
});
});
it("should call callback, after successful modified role", () => {
fetchMock.putOnce(ROLE1_URL, {
status: 204
});
fetchMock.getOnce(ROLE1_URL, role1);
let called = false;
const callMe = () => {
called = true;
};
const store = mockStore({});
return store.dispatch(modifyRole(role1, callMe)).then(() => {
expect(called).toBeTruthy();
});
});
it("should fail updating role on HTTP 500", () => {
fetchMock.putOnce(ROLE1_URL, {
status: 500
});
const store = mockStore({});
return store.dispatch(modifyRole(role1)).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(MODIFY_ROLE_PENDING);
expect(actions[1].type).toEqual(MODIFY_ROLE_FAILURE);
expect(actions[1].payload).toBeDefined();
});
});
it("should delete successfully role1", () => {
fetchMock.deleteOnce(ROLE1_URL, {
status: 204
});
const store = mockStore({});
return store.dispatch(deleteRole(role1)).then(() => {
const actions = store.getActions();
expect(actions.length).toBe(2);
expect(actions[0].type).toEqual(DELETE_ROLE_PENDING);
expect(actions[0].payload).toBe(role1);
expect(actions[1].type).toEqual(DELETE_ROLE_SUCCESS);
});
});
it("should call the callback, after successful delete", () => {
fetchMock.deleteOnce(ROLE1_URL, {
status: 204
});
let called = false;
const callMe = () => {
called = true;
};
const store = mockStore({});
return store.dispatch(deleteRole(role1, callMe)).then(() => {
expect(called).toBeTruthy();
});
});
it("should fail to delete role1", () => {
fetchMock.deleteOnce(ROLE1_URL, {
status: 500
});
const store = mockStore({});
return store.dispatch(deleteRole(role1)).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(DELETE_ROLE_PENDING);
expect(actions[0].payload).toBe(role1);
expect(actions[1].type).toEqual(DELETE_ROLE_FAILURE);
expect(actions[1].payload).toBeDefined();
});
});
});
describe("repository roles reducer", () => {
it("should update state correctly according to FETCH_ROLES_SUCCESS action", () => {
});
it("should set roleCreatePermission to true if update link is present", () => {
});
it("should not replace whole byNames map when fetching roles", () => {
});
it("should remove role from state when delete succeeds", () => {
});
it("should set roleCreatePermission to true if create link is present", () => {
});
it("should update state according to FETCH_ROLE_SUCCESS action", () => {
});
it("should affect roles state nor the state of other roles", () => {
});
});
describe("selector tests", () => {
it("should return an empty object", () => {
expect(selectListAsCollection({})).toEqual({});
expect(selectListAsCollection({ roles: { a: "a" } })).toEqual({});
});
it("should return a state slice collection", () => {
const collection = {
page: 3,
totalPages: 42
};
const state = {
roles: {
list: {
entry: collection
}
}
};
expect(selectListAsCollection(state)).toBe(collection);
});
it("should return false", () => {
expect(isPermittedToCreateRoles({})).toBe(false);
expect(isPermittedToCreateRoles({ roles: { list: { entry: {} } } })).toBe(
false
);
expect(
isPermittedToCreateRoles({
roles: { list: { entry: { roleCreatePermission: false } } }
})
).toBe(false);
});
it("should return true", () => {
const state = {
roles: {
list: {
entry: {
roleCreatePermission: true
}
}
}
};
expect(isPermittedToCreateRoles(state)).toBe(true);
});
it("should get roles from state", () => {
const state = {
roles: {
list: {
entries: ["a", "b"]
},
byNames: {
a: { name: "a" },
b: { name: "b" }
}
}
};
expect(getRolesFromState(state)).toEqual([{ name: "a" }, { name: "b" }]);
});
it("should return true, when fetch roles is pending", () => {
const state = {
pending: {
[FETCH_ROLES]: true
}
};
expect(isFetchRolesPending(state)).toEqual(true);
});
it("should return false, when fetch roles is not pending", () => {
expect(isFetchRolesPending({})).toEqual(false);
});
it("should return error when fetch roles did fail", () => {
const state = {
failure: {
[FETCH_ROLES]: error
}
};
expect(getFetchRolesFailure(state)).toEqual(error);
});
it("should return undefined when fetch roles did not fail", () => {
expect(getFetchRolesFailure({})).toBe(undefined);
});
it("should return true if create role is pending", () => {
const state = {
pending: {
[CREATE_ROLE]: true
}
};
expect(isCreateRolePending(state)).toBe(true);
});
it("should return false if create role is not pending", () => {
const state = {
pending: {
[CREATE_ROLE]: false
}
};
expect(isCreateRolePending(state)).toBe(false);
});
it("should return error when create role did fail", () => {
const state = {
failure: {
[CREATE_ROLE]: error
}
};
expect(getCreateRoleFailure(state)).toEqual(error);
});
it("should return undefined when create role did not fail", () => {
expect(getCreateRoleFailure({})).toBe(undefined);
});
it("should return role1", () => {
const state = {
roles: {
byNames: {
role1: role1
}
}
};
expect(getRoleByName(state, "role1")).toEqual(role1);
});
it("should return true, when fetch role2 is pending", () => {
const state = {
pending: {
[FETCH_ROLE + "/role2"]: true
}
};
expect(isFetchRolePending(state, "role2")).toEqual(true);
});
it("should return false, when fetch role2 is not pending", () => {
expect(isFetchRolePending({}, "role2")).toEqual(false);
});
it("should return error when fetch role2 did fail", () => {
const state = {
failure: {
[FETCH_ROLE + "/role2"]: error
}
};
expect(getFetchRoleFailure(state, "role2")).toEqual(error);
});
it("should return undefined when fetch role2 did not fail", () => {
expect(getFetchRoleFailure({}, "role2")).toBe(undefined);
});
it("should return true, when modify role1 is pending", () => {
const state = {
pending: {
[MODIFY_ROLE + "/role1"]: true
}
};
expect(isModifyRolePending(state, "role1")).toEqual(true);
});
it("should return false, when modify role1 is not pending", () => {
expect(isModifyRolePending({}, "role1")).toEqual(false);
});
it("should return error when modify role1 did fail", () => {
const state = {
failure: {
[MODIFY_ROLE + "/role1"]: error
}
};
expect(getModifyRoleFailure(state, "role1")).toEqual(error);
});
it("should return undefined when modify role1 did not fail", () => {
expect(getModifyRoleFailure({}, "role1")).toBe(undefined);
});
it("should return true, when delete role2 is pending", () => {
const state = {
pending: {
[DELETE_ROLE + "/role2"]: true
}
};
expect(isDeleteRolePending(state, "role2")).toEqual(true);
});
it("should return false, when delete role2 is not pending", () => {
expect(isDeleteRolePending({}, "role2")).toEqual(false);
});
it("should return error when delete role2 did fail", () => {
const state = {
failure: {
[DELETE_ROLE + "/role2"]: error
}
};
expect(getDeleteRoleFailure(state, "role2")).toEqual(error);
});
it("should return undefined when delete role2 did not fail", () => {
expect(getDeleteRoleFailure({}, "role2")).toBe(undefined);
});
});

View File

@@ -28,7 +28,7 @@ export const MODIFY_GROUP_SUCCESS = `${MODIFY_GROUP}_${types.SUCCESS_SUFFIX}`;
export const MODIFY_GROUP_FAILURE = `${MODIFY_GROUP}_${types.FAILURE_SUFFIX}`;
export const MODIFY_GROUP_RESET = `${MODIFY_GROUP}_${types.RESET_SUFFIX}`;
export const DELETE_GROUP = "scm/groups/DELETE";
export const DELETE_GROUP = "scm/groups/DELETE_GROUP";
export const DELETE_GROUP_PENDING = `${DELETE_GROUP}_${types.PENDING_SUFFIX}`;
export const DELETE_GROUP_SUCCESS = `${DELETE_GROUP}_${types.SUCCESS_SUFFIX}`;
export const DELETE_GROUP_FAILURE = `${DELETE_GROUP}_${types.FAILURE_SUFFIX}`;

View File

@@ -151,6 +151,11 @@ export function getSvnConfigLink(state: Object) {
return getLink(state, "svnConfig");
}
export function getRolesLink(state: Object) {
//return getLink(state, "availableRepositoryPermissions");
return "http://localhost:8081/scm/api/v2/repository-roles"; // TODO
}
export function getUserAutoCompleteLink(state: Object): string {
const link = getLinkCollection(state, "autocomplete").find(
i => i.name === "users"

View File

@@ -28,7 +28,7 @@ export const MODIFY_USER_SUCCESS = `${MODIFY_USER}_${types.SUCCESS_SUFFIX}`;
export const MODIFY_USER_FAILURE = `${MODIFY_USER}_${types.FAILURE_SUFFIX}`;
export const MODIFY_USER_RESET = `${MODIFY_USER}_${types.RESET_SUFFIX}`;
export const DELETE_USER = "scm/users/DELETE";
export const DELETE_USER = "scm/users/DELETE_USER";
export const DELETE_USER_PENDING = `${DELETE_USER}_${types.PENDING_SUFFIX}`;
export const DELETE_USER_SUCCESS = `${DELETE_USER}_${types.SUCCESS_SUFFIX}`;
export const DELETE_USER_FAILURE = `${DELETE_USER}_${types.FAILURE_SUFFIX}`;

View File

@@ -4,49 +4,49 @@ import thunk from "redux-thunk";
import fetchMock from "fetch-mock";
import reducer, {
CREATE_USER_FAILURE,
CREATE_USER_PENDING,
CREATE_USER_SUCCESS,
createUser,
DELETE_USER_FAILURE,
DELETE_USER_PENDING,
DELETE_USER_SUCCESS,
deleteUser,
deleteUserSuccess,
FETCH_USER_FAILURE,
FETCH_USER_PENDING,
isFetchUserPending,
FETCH_USER_SUCCESS,
FETCH_USERS_FAILURE,
FETCH_USERS,
FETCH_USERS_PENDING,
FETCH_USERS_SUCCESS,
FETCH_USERS_FAILURE,
FETCH_USER,
FETCH_USER_PENDING,
FETCH_USER_SUCCESS,
FETCH_USER_FAILURE,
CREATE_USER,
CREATE_USER_PENDING,
CREATE_USER_SUCCESS,
CREATE_USER_FAILURE,
MODIFY_USER,
MODIFY_USER_PENDING,
MODIFY_USER_SUCCESS,
MODIFY_USER_FAILURE,
DELETE_USER,
DELETE_USER_PENDING,
DELETE_USER_SUCCESS,
DELETE_USER_FAILURE,
fetchUsers,
getFetchUsersFailure,
getUsersFromState,
isFetchUsersPending,
fetchUsersSuccess,
fetchUserByLink,
fetchUserByName,
fetchUserSuccess,
isFetchUserPending,
getFetchUserFailure,
fetchUsers,
fetchUsersSuccess,
isFetchUsersPending,
selectListAsCollection,
isPermittedToCreateUsers,
MODIFY_USER,
MODIFY_USER_FAILURE,
MODIFY_USER_PENDING,
MODIFY_USER_SUCCESS,
modifyUser,
getUsersFromState,
FETCH_USERS,
getFetchUsersFailure,
FETCH_USER,
CREATE_USER,
createUser,
isCreateUserPending,
getCreateUserFailure,
getUserByName,
modifyUser,
isModifyUserPending,
getModifyUserFailure,
DELETE_USER,
deleteUser,
isDeleteUserPending,
getDeleteUserFailure
deleteUserSuccess,
getDeleteUserFailure,
selectListAsCollection,
isPermittedToCreateUsers
} from "./users";
const userZaphod = {
@@ -302,7 +302,7 @@ describe("users fetch()", () => {
});
it("should fail updating user on HTTP 500", () => {
fetchMock.putOnce("http://localhost:8081/api/v2/users/zaphod", {
fetchMock.putOnce(USER_ZAPHOD_URL, {
status: 500
});
@@ -316,7 +316,7 @@ describe("users fetch()", () => {
});
it("should delete successfully user zaphod", () => {
fetchMock.deleteOnce("http://localhost:8081/api/v2/users/zaphod", {
fetchMock.deleteOnce(USER_ZAPHOD_URL, {
status: 204
});
@@ -331,7 +331,7 @@ describe("users fetch()", () => {
});
it("should call the callback, after successful delete", () => {
fetchMock.deleteOnce("http://localhost:8081/api/v2/users/zaphod", {
fetchMock.deleteOnce(USER_ZAPHOD_URL, {
status: 204
});
@@ -347,7 +347,7 @@ describe("users fetch()", () => {
});
it("should fail to delete user zaphod", () => {
fetchMock.deleteOnce("http://localhost:8081/api/v2/users/zaphod", {
fetchMock.deleteOnce(USER_ZAPHOD_URL, {
status: 500
});