add modify functionality, state is not updated correctly right now

This commit is contained in:
Maren Süwer
2018-08-23 15:39:09 +02:00
parent e160ba48b5
commit 95288b5c63
6 changed files with 318 additions and 66 deletions

View File

@@ -4,17 +4,16 @@ import type { Permission } from "../../types/Permissions";
import { Checkbox, InputField } from "../../../components/forms/index";
import { DeleteButton, SubmitButton } from "../../../components/buttons/index";
import { translate } from "react-i18next";
import { Select } from "../../../components/forms";
import { Select } from "../../../components/forms/index";
type Props = {
submitForm: Permission => void,
permission: Permission,
t: string => string
};
type State = {
name: string,
type: string,
groupPermission: boolean
permission: Permission
};
class PermissionRowEditable extends React.Component<Props, State> {
@@ -22,9 +21,12 @@ class PermissionRowEditable extends React.Component<Props, State> {
super(props);
this.state = {
permission: {
name: "",
type: "READ",
groupPermission: false
groupPermission: false,
_links: {}
}
};
}
@@ -32,15 +34,21 @@ class PermissionRowEditable extends React.Component<Props, State> {
const { permission } = this.props;
if (permission) {
this.setState({
permission: {
name: permission.name,
type: permission.type,
groupPermission: permission.groupPermission
groupPermission: permission.groupPermission,
_links: permission._links
}
});
}
}
submit = (event: Event) => {
event.preventDefault();
this.props.submitForm(this.state.permission);
};
render() {
const { name, type, groupPermission } = this.state;
const { permission } = this.state;
const { t } = this.props;
const types = ["READ", "OWNER", "GROUP"];
@@ -52,25 +60,30 @@ class PermissionRowEditable extends React.Component<Props, State> {
<tr>
<td>
<InputField
value={name ? name : ""}
value={permission.name ? permission.name : ""}
onChange={this.handleNameChange}
/>
</td>
<td className="is-hidden-mobile">
<Select
onChange={this.handleTypeChange}
value={type ? type : ""}
value={permission.type ? permission.type : ""}
options={this.createSelectOptions(types)}
/>
</td>
<td>
<Checkbox
checked={groupPermission ? groupPermission : false}
checked={
permission.groupPermission ? permission.groupPermission : false
}
onChange={this.handleGroupPermissionChange}
/>
</td>
<td>
<SubmitButton label={t("edit-permission.save-button")} />
<SubmitButton
label={t("edit-permission.save-button")}
action={this.submit}
/>
</td>
<td>{deleteButton}</td>
</tr>
@@ -79,7 +92,10 @@ class PermissionRowEditable extends React.Component<Props, State> {
handleTypeChange = (type: string) => {
this.setState({
permission: {
...this.state.permission,
type: type
}
});
};
@@ -94,13 +110,19 @@ class PermissionRowEditable extends React.Component<Props, State> {
handleNameChange = (name: string) => {
this.setState({
permission: {
...this.state.permission,
name: name
}
});
};
handleGroupPermissionChange = (groupPermission: boolean) => {
this.setState({
permission: {
...this.state.permission,
groupPermission: groupPermission
}
});
};
}

View File

@@ -1,41 +0,0 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import PermissionRow from "./PermissionRow";
import type { PermissionCollection } from "../../types/Permissions";
import PermissionRowEditable from "./PermissionRowEditable";
type Props = {
t: string => string,
permissions: PermissionCollection
};
class PermissionsTable extends React.Component<Props> {
render() {
const { permissions, t } = this.props;
return (
<table className="table is-hoverable is-fullwidth">
<thead>
<tr>
<th>{t("permission.name")}</th>
<th className="is-hidden-mobile">{t("permission.type")}</th>
<th>{t("permission.group-permission")}</th>
</tr>
</thead>
<tbody>
{permissions.map((permission, index) => {
if (permission._links.update)
return (
<PermissionRowEditable key={index} permission={permission} />
);
else
return <PermissionRow key={index} permission={permission} />;
})}
</tbody>
</table>
);
}
}
export default translate("permissions")(PermissionsTable);

View File

@@ -11,7 +11,7 @@ import {
import Loading from "../../components/Loading";
import ErrorPage from "../../components/ErrorPage";
import type { PermissionCollection } from "../types/Permissions";
import PermissionsTable from "../components/table/PermissionsTable";
import PermissionsTable from "./PermissionsTable";
type Props = {
namespace: string,
@@ -36,7 +36,7 @@ class Permissions extends React.Component<Props> {
}
render() {
const { loading, error, permissions, t } = this.props;
const { loading, error, permissions, t, namespace, name } = this.props;
if (error) {
return (
<ErrorPage
@@ -52,7 +52,7 @@ class Permissions extends React.Component<Props> {
}
if (permissions.length > 0)
return <PermissionsTable permissions={permissions} />;
return <PermissionsTable permissions={permissions} namespace={namespace} name={name} />;
return <div />;
}

View File

@@ -0,0 +1,84 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import PermissionRow from "../components/table/PermissionRow";
import type { Permission, PermissionCollection } from "../types/Permissions";
import PermissionRowEditable from "../components/table/PermissionRowEditable";
import connect from "react-redux/es/connect/connect";
import { modifyPermission } from "../modules/permissions";
import type { History } from "history";
import {
getModifyRepoFailure,
isModifyRepoPending
} from "../../repos/modules/repos";
type Props = {
t: string => string,
permissions: PermissionCollection,
modifyPermission: (Permission, string, string, () => void) => void,
namespace: string,
name: string,
history: History
};
class PermissionsTable extends React.Component<Props> {
permissionsModified = () => {
const { history, name, namespace } = this.props;
history.push(`/repo/${namespace}/${name}/permissions`);
};
render() {
const { permissions, t, namespace, name } = this.props;
return (
<table className="table is-hoverable is-fullwidth">
<thead>
<tr>
<th>{t("permission.name")}</th>
<th className="is-hidden-mobile">{t("permission.type")}</th>
<th>{t("permission.group-permission")}</th>
</tr>
</thead>
<tbody>
{permissions.map((permission, index) => {
if (permission._links.update)
return (
<PermissionRowEditable
key={index}
permission={permission}
submitForm={permission => {
this.props.modifyPermission(
permission,
namespace,
name,
this.permissionsModified
);
}}
/>
);
else return <PermissionRow key={index} permission={permission} />;
})}
</tbody>
</table>
);
}
}
const mapStateToProps = (state, ownProps) => {};
const mapDispatchToProps = dispatch => {
return {
modifyPermission: (
permission: Permission,
namespace: string,
name: string,
callback: () => void
) => {
dispatch(modifyPermission(permission, namespace, name, callback));
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(translate("permissions")(PermissionsTable));

View File

@@ -2,7 +2,7 @@
import { apiClient } from "../../apiclient";
import * as types from "../../modules/types";
import type { Action } from "../../types/Action";
import type { PermissionCollection } from "../types/Permissions";
import type { PermissionCollection, Permission } from "../types/Permissions";
import { isPending } from "../../modules/pending";
import { getFailure } from "../../modules/failure";
@@ -16,9 +16,19 @@ export const FETCH_PERMISSIONS_SUCCESS = `${FETCH_PERMISSIONS}_${
export const FETCH_PERMISSIONS_FAILURE = `${FETCH_PERMISSIONS}_${
types.FAILURE_SUFFIX
}`;
export const MODIFY_PERMISSION = "scm/permissions/MODFIY_PERMISSION";
export const MODIFY_PERMISSION_PENDING = `${MODIFY_PERMISSION}_${
types.PENDING_SUFFIX
}`;
export const MODIFY_PERMISSION_SUCCESS = `${MODIFY_PERMISSION}_${
types.SUCCESS_SUFFIX
}`;
export const MODIFY_PERMISSION_FAILURE = `${MODIFY_PERMISSION}_${
types.FAILURE_SUFFIX
}`;
const REPOS_URL = "repositories";
const PERMISSIONS_URL = "permissions";
const CONTENT_TYPE = "application/vnd.scmm-permission+json";
// fetch permissions
@@ -79,6 +89,79 @@ export function fetchPermissionsFailure(
};
}
// modify permission
export function modifyPermission(
permission: Permission,
namespace: string,
name: string,
callback?: () => void
) {
return function(dispatch: any) {
dispatch(modifyPermissionPending(permission, namespace, name));
return apiClient
.put(permission._links.update.href, permission, CONTENT_TYPE)
.then(() => {
dispatch(modifyPermissionSuccess(permission, namespace, name));
if (callback) {
callback();
}
})
.catch(cause => {
const error = new Error(
`failed to modify permission: ${cause.message}`
);
dispatch(modifyPermissionFailure(permission, error, namespace, name));
});
};
}
export function modifyPermissionPending(
permission: Permission,
namespace: string,
name: string
): Action {
return {
type: MODIFY_PERMISSION_PENDING,
payload: permission,
itemId: namespace + "/" + name
};
}
export function modifyPermissionSuccess(
permission: Permission,
namespace: string,
name: string
): Action {
return {
type: MODIFY_PERMISSION_SUCCESS,
payload: permission,
itemId: namespace + "/" + name
};
}
export function modifyPermissionFailure(
permission: Permission,
error: Error,
namespace: string,
name: string
): Action {
return {
type: MODIFY_PERMISSION_FAILURE,
payload: { error, permission },
itemId: namespace + "/" + name
};
}
function newPermissions(modifiedPermission: Permission) {
let newPermissions = [];
newPermissions[0] = modifiedPermission;
return {
newPermissions
};
}
// reducer
export default function reducer(
state: Object = {},
@@ -94,6 +177,12 @@ export default function reducer(
...state,
[action.itemId]: action.payload
};
case MODIFY_PERMISSION_SUCCESS:
const newPermission = newPermissions(action.payload);
return {
...state,
[action.itemId]: newPermission
};
default:
return state;
}

View File

@@ -8,12 +8,18 @@ import reducer, {
getPermissionsOfRepo,
isFetchPermissionsPending,
getFetchPermissionsFailure,
modifyPermission,
modifyPermissionSuccess,
MODIFY_PERMISSION_FAILURE,
MODIFY_PERMISSION_PENDING,
FETCH_PERMISSIONS,
FETCH_PERMISSIONS_PENDING,
FETCH_PERMISSIONS_SUCCESS,
FETCH_PERMISSIONS_FAILURE
FETCH_PERMISSIONS_FAILURE,
MODIFY_PERMISSION_SUCCESS
} from "./permissions";
import type { Permission, PermissionCollection } from "../types/Permissions";
import { modifyRepoSuccess } from "../../repos/modules/repos";
const hitchhiker_puzzle42Permission_user_eins: Permission = {
name: "user_eins",
@@ -114,6 +120,81 @@ describe("permission fetch", () => {
expect(actions[1].payload).toBeDefined();
});
});
it("should successfully modify user_eins permission", () => {
fetchMock.putOnce(
hitchhiker_puzzle42Permission_user_eins._links.update.href,
{
status: 204
}
);
let editedPermission = { ...hitchhiker_puzzle42Permission_user_eins };
editedPermission.type = "OWNER";
const store = mockStore({});
return store
.dispatch(modifyPermission(editedPermission, "hitchhiker", "puzzle42"))
.then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
expect(actions[1].type).toEqual(MODIFY_PERMISSION_SUCCESS);
});
});
it("should successfully modify user_eins permission and call the callback", () => {
fetchMock.putOnce(
hitchhiker_puzzle42Permission_user_eins._links.update.href,
{
status: 204
}
);
let editedPermission = { ...hitchhiker_puzzle42Permission_user_eins };
editedPermission.type = "OWNER";
const store = mockStore({});
let called = false;
const callback = () => {
called = true;
};
return store
.dispatch(
modifyPermission(editedPermission, "hitchhiker", "puzzle42", callback)
)
.then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
expect(actions[1].type).toEqual(MODIFY_PERMISSION_SUCCESS);
expect(called).toBe(true);
});
});
it("should fail modifying on HTTP 500", () => {
fetchMock.putOnce(
hitchhiker_puzzle42Permission_user_eins._links.update.href,
{
status: 500
}
);
let editedPermission = { ...hitchhiker_puzzle42Permission_user_eins };
editedPermission.type = "OWNER";
const store = mockStore({});
return store
.dispatch(modifyPermission(editedPermission, "hitchhiker", "puzzle42"))
.then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
expect(actions[1].type).toEqual(MODIFY_PERMISSION_FAILURE);
expect(actions[1].payload).toBeDefined();
});
});
});
describe("permissions reducer", () => {
@@ -145,6 +226,23 @@ describe("permissions reducer", () => {
hitchhiker_puzzle42Permissions
);
});
it("should update permission", () => {
const oldState = {
permissions: {
"hitchhiker/puzzle42": {
hitchhiker_puzzle42Permission_user_eins
}
}
};
let permissionEdited = { ...hitchhiker_puzzle42Permission_user_eins };
permissionEdited.type = "OWNER";
const newState = reducer(
oldState,
modifyPermissionSuccess(permissionEdited, "hitchhiker", "puzzle42")
);
expect(newState["hitchhiker/puzzle42"]).toEqual(permissionEdited);
});
});
describe("permissions selectors", () => {