use reflow to migrate from flow to typescript

This commit is contained in:
Sebastian Sdorra
2019-10-19 16:38:07 +02:00
parent f7b8050dfa
commit 6e7a08a3bb
495 changed files with 14239 additions and 13766 deletions

View File

@@ -1,26 +1,25 @@
// @flow
import React from "react";
import type { User } from "@scm-manager/ui-types";
import React from 'react';
import { User } from '@scm-manager/ui-types';
import {
SubmitButton,
Notification,
ErrorNotification,
PasswordConfirmation
} from "@scm-manager/ui-components";
import { translate } from "react-i18next";
import { setPassword } from "./setPassword";
PasswordConfirmation,
} from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
import { setPassword } from './setPassword';
type Props = {
user: User,
t: string => string
user: User;
t: (p: string) => string;
};
type State = {
password: string,
loading: boolean,
error?: Error,
passwordChanged: boolean,
passwordValid: boolean
password: string;
loading: boolean;
error?: Error;
passwordChanged: boolean;
passwordValid: boolean;
};
class SetUserPassword extends React.Component<Props, State> {
@@ -28,20 +27,20 @@ class SetUserPassword extends React.Component<Props, State> {
super(props);
this.state = {
password: "",
password: '',
loading: false,
passwordConfirmationError: false,
validatePasswordError: false,
validatePassword: "",
validatePassword: '',
passwordChanged: false,
passwordValid: false
passwordValid: false,
};
}
setLoadingState = () => {
this.setState({
...this.state,
loading: true
loading: true,
});
};
@@ -49,7 +48,7 @@ class SetUserPassword extends React.Component<Props, State> {
this.setState({
...this.state,
error: error,
loading: false
loading: false,
});
};
@@ -58,7 +57,7 @@ class SetUserPassword extends React.Component<Props, State> {
...this.state,
loading: false,
passwordChanged: true,
password: ""
password: '',
});
};
@@ -89,8 +88,8 @@ class SetUserPassword extends React.Component<Props, State> {
if (passwordChanged) {
message = (
<Notification
type={"success"}
children={t("singleUserPassword.setPasswordSuccessful")}
type={'success'}
children={t('singleUserPassword.setPasswordSuccessful')}
onClose={() => this.onClose()}
/>
);
@@ -103,14 +102,14 @@ class SetUserPassword extends React.Component<Props, State> {
{message}
<PasswordConfirmation
passwordChanged={this.passwordChanged}
key={this.state.passwordChanged ? "changed" : "unchanged"}
key={this.state.passwordChanged ? 'changed' : 'unchanged'}
/>
<div className="columns">
<div className="column">
<SubmitButton
disabled={!this.state.passwordValid}
loading={loading}
label={t("singleUserPassword.button")}
label={t('singleUserPassword.button')}
/>
</div>
</div>
@@ -122,16 +121,16 @@ class SetUserPassword extends React.Component<Props, State> {
this.setState({
...this.state,
password,
passwordValid: !!password && passwordValid
passwordValid: !!password && passwordValid,
});
};
onClose = () => {
this.setState({
...this.state,
passwordChanged: false
passwordChanged: false,
});
};
}
export default translate("users")(SetUserPassword);
export default translate('users')(SetUserPassword);

View File

@@ -1,30 +1,29 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import type { User } from "@scm-manager/ui-types";
import React from 'react';
import { translate } from 'react-i18next';
import { User } from '@scm-manager/ui-types';
import {
Subtitle,
Checkbox,
InputField,
PasswordConfirmation,
SubmitButton,
validation as validator
} from "@scm-manager/ui-components";
import * as userValidator from "./userValidation";
validation as validator,
} from '@scm-manager/ui-components';
import * as userValidator from './userValidation';
type Props = {
submitForm: User => void,
user?: User,
loading?: boolean,
t: string => string
submitForm: (p: User) => void;
user?: User;
loading?: boolean;
t: (p: string) => string;
};
type State = {
user: User,
mailValidationError: boolean,
nameValidationError: boolean,
displayNameValidationError: boolean,
passwordValid: boolean
user: User;
mailValidationError: boolean;
nameValidationError: boolean;
displayNameValidationError: boolean;
passwordValid: boolean;
};
class UserForm extends React.Component<Props, State> {
@@ -33,24 +32,28 @@ class UserForm extends React.Component<Props, State> {
this.state = {
user: {
name: "",
displayName: "",
mail: "",
password: "",
name: '',
displayName: '',
mail: '',
password: '',
active: true,
_links: {}
_links: {},
},
mailValidationError: false,
displayNameValidationError: false,
nameValidationError: false,
passwordValid: false
passwordValid: false,
};
}
componentDidMount() {
const { user } = this.props;
if (user) {
this.setState({ user: { ...user } });
this.setState({
user: {
...user,
},
});
}
}
@@ -118,12 +121,12 @@ class UserForm extends React.Component<Props, State> {
nameField = (
<div className="column is-half">
<InputField
label={t("user.name")}
label={t('user.name')}
onChange={this.handleUsernameChange}
value={user ? user.name : ""}
value={user ? user.name : ''}
validationError={this.state.nameValidationError}
errorMessage={t("validation.name-invalid")}
helpText={t("help.usernameHelpText")}
errorMessage={t('validation.name-invalid')}
helpText={t('help.usernameHelpText')}
/>
</div>
);
@@ -133,7 +136,7 @@ class UserForm extends React.Component<Props, State> {
);
} else {
// edit existing user
subtitle = <Subtitle subtitle={t("userForm.subtitle")} />;
subtitle = <Subtitle subtitle={t('userForm.subtitle')} />;
}
return (
<>
@@ -143,22 +146,22 @@ class UserForm extends React.Component<Props, State> {
{nameField}
<div className="column is-half">
<InputField
label={t("user.displayName")}
label={t('user.displayName')}
onChange={this.handleDisplayNameChange}
value={user ? user.displayName : ""}
value={user ? user.displayName : ''}
validationError={this.state.displayNameValidationError}
errorMessage={t("validation.displayname-invalid")}
helpText={t("help.displayNameHelpText")}
errorMessage={t('validation.displayname-invalid')}
helpText={t('help.displayNameHelpText')}
/>
</div>
<div className="column is-half">
<InputField
label={t("user.mail")}
label={t('user.mail')}
onChange={this.handleEmailChange}
value={user ? user.mail : ""}
value={user ? user.mail : ''}
validationError={this.state.mailValidationError}
errorMessage={t("validation.mail-invalid")}
helpText={t("help.mailHelpText")}
errorMessage={t('validation.mail-invalid')}
helpText={t('help.mailHelpText')}
/>
</div>
</div>
@@ -166,10 +169,10 @@ class UserForm extends React.Component<Props, State> {
<div className="columns">
<div className="column">
<Checkbox
label={t("user.active")}
label={t('user.active')}
onChange={this.handleActiveChange}
checked={user ? user.active : false}
helpText={t("help.activeHelpText")}
helpText={t('help.activeHelpText')}
/>
</div>
</div>
@@ -178,7 +181,7 @@ class UserForm extends React.Component<Props, State> {
<SubmitButton
disabled={!this.isValid()}
loading={loading}
label={t("userForm.button")}
label={t('userForm.button')}
/>
</div>
</div>
@@ -190,36 +193,53 @@ class UserForm extends React.Component<Props, State> {
handleUsernameChange = (name: string) => {
this.setState({
nameValidationError: !validator.isNameValid(name),
user: { ...this.state.user, name }
user: {
...this.state.user,
name,
},
});
};
handleDisplayNameChange = (displayName: string) => {
this.setState({
displayNameValidationError: !userValidator.isDisplayNameValid(
displayName
displayName,
),
user: { ...this.state.user, displayName }
user: {
...this.state.user,
displayName,
},
});
};
handleEmailChange = (mail: string) => {
this.setState({
mailValidationError: !validator.isMailValid(mail),
user: { ...this.state.user, mail }
user: {
...this.state.user,
mail,
},
});
};
handlePasswordChange = (password: string, passwordValid: boolean) => {
this.setState({
user: { ...this.state.user, password },
passwordValid: !this.isFalsy(password) && passwordValid
user: {
...this.state.user,
password,
},
passwordValid: !this.isFalsy(password) && passwordValid,
});
};
handleActiveChange = (active: boolean) => {
this.setState({ user: { ...this.state.user, active } });
this.setState({
user: {
...this.state.user,
active,
},
});
};
}
export default translate("users")(UserForm);
export default translate('users')(UserForm);

View File

@@ -1,28 +0,0 @@
//@flow
import React from "react";
import type { User } from "@scm-manager/ui-types";
import { NavLink } from "@scm-manager/ui-components";
import { translate } from "react-i18next";
type Props = {
user: User,
editUrl: String,
t: string => string
};
class EditUserNavLink extends React.Component<Props> {
isEditable = () => {
return this.props.user._links.update;
};
render() {
const { t, editUrl } = this.props;
if (!this.isEditable()) {
return null;
}
return <NavLink to={editUrl} label={t("singleUser.menu.generalNavLink")} />;
}
}
export default translate("users")(EditUserNavLink);

View File

@@ -1,27 +0,0 @@
import React from "react";
import { shallow } from "enzyme";
import "@scm-manager/ui-tests/enzyme";
import "@scm-manager/ui-tests/i18n";
import EditUserNavLink from "./EditUserNavLink";
it("should render nothing, if the edit link is missing", () => {
const user = {
_links: {}
};
const navLink = shallow(<EditUserNavLink user={user} editUrl='/user/edit'/>);
expect(navLink.text()).toBe("");
});
it("should render the navLink", () => {
const user = {
_links: {
update: {
href: "/users"
}
}
};
const navLink = shallow(<EditUserNavLink user={user} editUrl='/user/edit'/>);
expect(navLink.text()).not.toBe("");
});

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { shallow } from 'enzyme';
import '@scm-manager/ui-tests/enzyme';
import '@scm-manager/ui-tests/i18n';
import EditUserNavLink from './EditUserNavLink';
it('should render nothing, if the edit link is missing', () => {
const user = {
_links: {},
};
const navLink = shallow(<EditUserNavLink user={user} editUrl="/user/edit" />);
expect(navLink.text()).toBe('');
});
it('should render the navLink', () => {
const user = {
_links: {
update: {
href: '/users',
},
},
};
const navLink = shallow(<EditUserNavLink user={user} editUrl="/user/edit" />);
expect(navLink.text()).not.toBe('');
});

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { User } from '@scm-manager/ui-types';
import { NavLink } from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
type Props = {
user: User;
editUrl: string;
t: (p: string) => string;
};
class EditUserNavLink extends React.Component<Props> {
isEditable = () => {
return this.props.user._links.update;
};
render() {
const { t, editUrl } = this.props;
if (!this.isEditable()) {
return null;
}
return <NavLink to={editUrl} label={t('singleUser.menu.generalNavLink')} />;
}
}
export default translate('users')(EditUserNavLink);

View File

@@ -1,28 +0,0 @@
//@flow
import React from "react";
import { translate } from "react-i18next";
import type { User } from "@scm-manager/ui-types";
import { NavLink } from "@scm-manager/ui-components";
type Props = {
t: string => string,
user: User,
passwordUrl: String
};
class ChangePasswordNavLink extends React.Component<Props> {
render() {
const { t, passwordUrl } = this.props;
if (!this.hasPermissionToSetPassword()) {
return null;
}
return <NavLink to={passwordUrl} label={t("singleUser.menu.setPasswordNavLink")} />;
}
hasPermissionToSetPassword = () => {
return this.props.user._links.password;
};
}
export default translate("users")(ChangePasswordNavLink);

View File

@@ -1,31 +0,0 @@
import React from "react";
import { shallow } from "enzyme";
import "@scm-manager/ui-tests/enzyme";
import "@scm-manager/ui-tests/i18n";
import ChangePasswordNavLink from "./SetPasswordNavLink";
it("should render nothing, if the password link is missing", () => {
const user = {
_links: {}
};
const navLink = shallow(
<ChangePasswordNavLink user={user} passwordUrl="/user/password" />
);
expect(navLink.text()).toBe("");
});
it("should render the navLink", () => {
const user = {
_links: {
password: {
href: "/password"
}
}
};
const navLink = shallow(
<ChangePasswordNavLink user={user} passwordUrl="/user/password" />
);
expect(navLink.text()).not.toBe("");
});

View File

@@ -0,0 +1,31 @@
import React from 'react';
import { shallow } from 'enzyme';
import '@scm-manager/ui-tests/enzyme';
import '@scm-manager/ui-tests/i18n';
import ChangePasswordNavLink from './SetPasswordNavLink';
it('should render nothing, if the password link is missing', () => {
const user = {
_links: {},
};
const navLink = shallow(
<ChangePasswordNavLink user={user} passwordUrl="/user/password" />,
);
expect(navLink.text()).toBe('');
});
it('should render the navLink', () => {
const user = {
_links: {
password: {
href: '/password',
},
},
};
const navLink = shallow(
<ChangePasswordNavLink user={user} passwordUrl="/user/password" />,
);
expect(navLink.text()).not.toBe('');
});

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { translate } from 'react-i18next';
import { User } from '@scm-manager/ui-types';
import { NavLink } from '@scm-manager/ui-components';
type Props = {
t: (p: string) => string;
user: User;
passwordUrl: string;
};
class ChangePasswordNavLink extends React.Component<Props> {
render() {
const { t, passwordUrl } = this.props;
if (!this.hasPermissionToSetPassword()) {
return null;
}
return (
<NavLink
to={passwordUrl}
label={t('singleUser.menu.setPasswordNavLink')}
/>
);
}
hasPermissionToSetPassword = () => {
return this.props.user._links.password;
};
}
export default translate('users')(ChangePasswordNavLink);

View File

@@ -1,28 +0,0 @@
//@flow
import React from "react";
import { translate } from "react-i18next";
import type { User } from "@scm-manager/ui-types";
import { NavLink } from "@scm-manager/ui-components";
type Props = {
t: string => string,
user: User,
permissionsUrl: String
};
class ChangePermissionNavLink extends React.Component<Props> {
render() {
const { t, permissionsUrl } = this.props;
if (!this.hasPermissionToSetPermission()) {
return null;
}
return <NavLink to={permissionsUrl} label={t("singleUser.menu.setPermissionsNavLink")} />;
}
hasPermissionToSetPermission = () => {
return this.props.user._links.permissions;
};
}
export default translate("users")(ChangePermissionNavLink);

View File

@@ -1,31 +0,0 @@
import React from "react";
import "@scm-manager/ui-tests/enzyme";
import "@scm-manager/ui-tests/i18n";
import SetPermissionsNavLink from "./SetPermissionsNavLink";
import { shallow } from "enzyme";
it("should render nothing, if the permissions link is missing", () => {
const user = {
_links: {}
};
const navLink = shallow(
<SetPermissionsNavLink user={user} permissionsUrl="/user/permissions" />
);
expect(navLink.text()).toBe("");
});
it("should render the navLink", () => {
const user = {
_links: {
permissions: {
href: "/permissions"
}
}
};
const navLink = shallow(
<SetPermissionsNavLink user={user} permissionsUrl="/user/permissions" />
);
expect(navLink.text()).not.toBe("");
});

View File

@@ -0,0 +1,31 @@
import React from 'react';
import '@scm-manager/ui-tests/enzyme';
import '@scm-manager/ui-tests/i18n';
import SetPermissionsNavLink from './SetPermissionsNavLink';
import { shallow } from 'enzyme';
it('should render nothing, if the permissions link is missing', () => {
const user = {
_links: {},
};
const navLink = shallow(
<SetPermissionsNavLink user={user} permissionsUrl="/user/permissions" />,
);
expect(navLink.text()).toBe('');
});
it('should render the navLink', () => {
const user = {
_links: {
permissions: {
href: '/permissions',
},
},
};
const navLink = shallow(
<SetPermissionsNavLink user={user} permissionsUrl="/user/permissions" />,
);
expect(navLink.text()).not.toBe('');
});

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { translate } from 'react-i18next';
import { User } from '@scm-manager/ui-types';
import { NavLink } from '@scm-manager/ui-components';
type Props = {
t: (p: string) => string;
user: User;
permissionsUrl: string;
};
class ChangePermissionNavLink extends React.Component<Props> {
render() {
const { t, permissionsUrl } = this.props;
if (!this.hasPermissionToSetPermission()) {
return null;
}
return (
<NavLink
to={permissionsUrl}
label={t('singleUser.menu.setPermissionsNavLink')}
/>
);
}
hasPermissionToSetPermission = () => {
return this.props.user._links.permissions;
};
}
export default translate('users')(ChangePermissionNavLink);

View File

@@ -1,3 +0,0 @@
export { default as EditUserNavLink } from "./EditUserNavLink";
export { default as SetPasswordNavLink } from "./SetPasswordNavLink";
export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink";

View File

@@ -0,0 +1,3 @@
export { default as EditUserNavLink } from './EditUserNavLink';
export { default as SetPasswordNavLink } from './SetPasswordNavLink';
export { default as SetPermissionsNavLink } from './SetPermissionsNavLink';

View File

@@ -1,13 +0,0 @@
//@flow
import { apiClient } from "@scm-manager/ui-components";
export const CONTENT_TYPE_PASSWORD_OVERWRITE =
"application/vnd.scmm-passwordOverwrite+json;v=2";
export function setPassword(url: string, password: string) {
return apiClient
.put(url, { newPassword: password }, CONTENT_TYPE_PASSWORD_OVERWRITE)
.then(response => {
return response;
});
}

View File

@@ -1,25 +0,0 @@
//@flow
import fetchMock from "fetch-mock";
import { CONTENT_TYPE_PASSWORD_OVERWRITE, setPassword } from "./setPassword";
describe("password change", () => {
const SET_PASSWORD_URL = "/users/testuser/password";
const newPassword = "testpw123";
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it("should set password", done => {
fetchMock.put("/api/v2" + SET_PASSWORD_URL, 204, {
headers: {
"content-type": CONTENT_TYPE_PASSWORD_OVERWRITE
}
});
setPassword(SET_PASSWORD_URL, newPassword).then(content => {
done();
});
});
});

View File

@@ -0,0 +1,24 @@
import fetchMock from 'fetch-mock';
import { CONTENT_TYPE_PASSWORD_OVERWRITE, setPassword } from './setPassword';
describe('password change', () => {
const SET_PASSWORD_URL = '/users/testuser/password';
const newPassword = 'testpw123';
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it('should set password', done => {
fetchMock.put('/api/v2' + SET_PASSWORD_URL, 204, {
headers: {
'content-type': CONTENT_TYPE_PASSWORD_OVERWRITE,
},
});
setPassword(SET_PASSWORD_URL, newPassword).then(content => {
done();
});
});
});

View File

@@ -0,0 +1,18 @@
import { apiClient } from '@scm-manager/ui-components';
export const CONTENT_TYPE_PASSWORD_OVERWRITE =
'application/vnd.scmm-passwordOverwrite+json;v=2';
export function setPassword(url: string, password: string) {
return apiClient
.put(
url,
{
newPassword: password,
},
CONTENT_TYPE_PASSWORD_OVERWRITE,
)
.then(response => {
return response;
});
}

View File

@@ -1,12 +1,11 @@
//@flow
import React from "react";
import type { User } from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import { Checkbox, MailLink, DateFromNow } from "@scm-manager/ui-components";
import React from 'react';
import { User } from '@scm-manager/ui-types';
import { translate } from 'react-i18next';
import { Checkbox, MailLink, DateFromNow } from '@scm-manager/ui-components';
type Props = {
user: User,
t: string => string
user: User;
t: (p: string) => string;
};
class Details extends React.Component<Props> {
@@ -16,37 +15,37 @@ class Details extends React.Component<Props> {
<table className="table">
<tbody>
<tr>
<th>{t("user.name")}</th>
<th>{t('user.name')}</th>
<td>{user.name}</td>
</tr>
<tr>
<th>{t("user.displayName")}</th>
<th>{t('user.displayName')}</th>
<td>{user.displayName}</td>
</tr>
<tr>
<th>{t("user.mail")}</th>
<th>{t('user.mail')}</th>
<td>
<MailLink address={user.mail} />
</td>
</tr>
<tr>
<th>{t("user.active")}</th>
<th>{t('user.active')}</th>
<td>
<Checkbox checked={user.active} />
</td>
</tr>
<tr>
<th>{t("user.type")}</th>
<th>{t('user.type')}</th>
<td>{user.type}</td>
</tr>
<tr>
<th>{t("user.creationDate")}</th>
<th>{t('user.creationDate')}</th>
<td>
<DateFromNow date={user.creationDate} />
</td>
</tr>
<tr>
<th>{t("user.lastModified")}</th>
<th>{t('user.lastModified')}</th>
<td>
<DateFromNow date={user.lastModified} />
</td>
@@ -57,4 +56,4 @@ class Details extends React.Component<Props> {
}
}
export default translate("users")(Details);
export default translate('users')(Details);

View File

@@ -1,43 +0,0 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import { Link } from "react-router-dom";
import type { User } from "@scm-manager/ui-types";
import { Icon } from "@scm-manager/ui-components";
type Props = {
user: User,
// context props
t: string => string
};
class UserRow extends React.Component<Props> {
renderLink(to: string, label: string) {
return <Link to={to}>{label}</Link>;
}
render() {
const { user, t } = this.props;
const to = `/user/${user.name}`;
const iconType = user.active ? (
<Icon title={t("user.active")} name="user" />
) : (
<Icon title={t("user.inactive")} name="user-slash" />
);
return (
<tr className={user.active ? "border-is-green" : "border-is-yellow"}>
<td>{iconType} {this.renderLink(to, user.name)}</td>
<td className="is-hidden-mobile">
{this.renderLink(to, user.displayName)}
</td>
<td>
<a href={`mailto:${user.mail}`}>{user.mail}</a>
</td>
</tr>
);
}
}
export default translate("users")(UserRow);

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Link } from 'react-router-dom';
import { User } from '@scm-manager/ui-types';
import { Icon } from '@scm-manager/ui-components';
type Props = {
user: User;
// context props
t: (p: string) => string;
};
class UserRow extends React.Component<Props> {
renderLink(to: string, label: string) {
return <Link to={to}>{label}</Link>;
}
render() {
const { user, t } = this.props;
const to = `/user/${user.name}`;
const iconType = user.active ? (
<Icon title={t('user.active')} name="user" />
) : (
<Icon title={t('user.inactive')} name="user-slash" />
);
return (
<tr className={user.active ? 'border-is-green' : 'border-is-yellow'}>
<td>
{iconType} {this.renderLink(to, user.name)}
</td>
<td className="is-hidden-mobile">
{this.renderLink(to, user.displayName)}
</td>
<td>
<a href={`mailto:${user.mail}`}>{user.mail}</a>
</td>
</tr>
);
}
}
export default translate('users')(UserRow);

View File

@@ -1,12 +1,11 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import UserRow from "./UserRow";
import type { User } from "@scm-manager/ui-types";
import React from 'react';
import { translate } from 'react-i18next';
import UserRow from './UserRow';
import { User } from '@scm-manager/ui-types';
type Props = {
t: string => string,
users: User[]
t: (p: string) => string;
users: User[];
};
class UserTable extends React.Component<Props> {
@@ -16,9 +15,9 @@ class UserTable extends React.Component<Props> {
<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.name')}</th>
<th>{t('user.displayName')}</th>
<th>{t('user.mail')}</th>
</tr>
</thead>
<tbody>
@@ -31,4 +30,4 @@ class UserTable extends React.Component<Props> {
}
}
export default translate("users")(UserTable);
export default translate('users')(UserTable);

View File

@@ -1,3 +0,0 @@
export { default as Details } from "./Details";
export { default as UserRow } from "./UserRow";
export { default as UserTable } from "./UserTable";

View File

@@ -0,0 +1,3 @@
export { default as Details } from './Details';
export { default as UserRow } from './UserRow';
export { default as UserTable } from './UserTable';

View File

@@ -1,40 +0,0 @@
// @flow
import * as validator from "./userValidation";
describe("test displayName validation", () => {
it("should return false", () => {
expect(validator.isDisplayNameValid("")).toBe(false);
});
it("should return true", () => {
// valid names taken from ValidationUtilTest.java
const validNames = [
"Arthur Dent",
"Tricia.McMillan@hitchhiker.com",
"Ford Prefect (ford.prefect@hitchhiker.com)",
"Zaphod Beeblebrox <zaphod.beeblebrox@hitchhiker.com>",
"Marvin, der depressive Roboter"
];
for (let name of validNames) {
expect(validator.isDisplayNameValid(name)).toBe(true);
}
});
});
describe("test password validation", () => {
it("should return false", () => {
// invalid taken from ValidationUtilTest.java
const invalid = ["", "abc", "aaabbbcccdddeeefffggghhhiiijjjkkk"];
for (let password of invalid) {
expect(validator.isPasswordValid(password)).toBe(false);
}
});
it("should return true", () => {
// valid taken from ValidationUtilTest.java
const valid = ["secret123", "mySuperSecretPassword"];
for (let password of valid) {
expect(validator.isPasswordValid(password)).toBe(true);
}
});
});

View File

@@ -0,0 +1,39 @@
import * as validator from './userValidation';
describe('test displayName validation', () => {
it('should return false', () => {
expect(validator.isDisplayNameValid('')).toBe(false);
});
it('should return true', () => {
// valid names taken from ValidationUtilTest.java
const validNames = [
'Arthur Dent',
'Tricia.McMillan@hitchhiker.com',
'Ford Prefect (ford.prefect@hitchhiker.com)',
'Zaphod Beeblebrox <zaphod.beeblebrox@hitchhiker.com>',
'Marvin, der depressive Roboter',
];
for (let name of validNames) {
expect(validator.isDisplayNameValid(name)).toBe(true);
}
});
});
describe('test password validation', () => {
it('should return false', () => {
// invalid taken from ValidationUtilTest.java
const invalid = ['', 'abc', 'aaabbbcccdddeeefffggghhhiiijjjkkk'];
for (let password of invalid) {
expect(validator.isPasswordValid(password)).toBe(false);
}
});
it('should return true', () => {
// valid taken from ValidationUtilTest.java
const valid = ['secret123', 'mySuperSecretPassword'];
for (let password of valid) {
expect(validator.isPasswordValid(password)).toBe(true);
}
});
});

View File

@@ -1,6 +1,4 @@
// @flow
import { validation } from "@scm-manager/ui-components";
import { validation } from '@scm-manager/ui-components';
const { isNameValid, isMailValid, isPathValid } = validation;

View File

@@ -1,31 +1,30 @@
//@flow
import React from "react";
import { connect } from "react-redux";
import UserForm from "../components/UserForm";
import type { User } from "@scm-manager/ui-types";
import type { History } from "history";
import React from 'react';
import { connect } from 'react-redux';
import UserForm from '../components/UserForm';
import { User } from '@scm-manager/ui-types';
import { History } from 'history';
import {
createUser,
createUserReset,
isCreateUserPending,
getCreateUserFailure
} from "../modules/users";
import { Page } from "@scm-manager/ui-components";
import { translate } from "react-i18next";
import { getUsersLink } from "../../modules/indexResource";
getCreateUserFailure,
} from '../modules/users';
import { Page } from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
import { getUsersLink } from '../../modules/indexResource';
type Props = {
loading?: boolean,
error?: Error,
usersLink: string,
loading?: boolean;
error?: Error;
usersLink: string;
// dispatcher functions
addUser: (link: string, user: User, callback?: () => void) => void,
resetForm: () => void,
addUser: (link: string, user: User, callback?: () => void) => void;
resetForm: () => void;
// context objects
t: string => string,
history: History
t: (p: string) => string;
history: History;
};
class CreateUser extends React.Component<Props> {
@@ -35,12 +34,12 @@ class CreateUser extends React.Component<Props> {
userCreated = (user: User) => {
const { history } = this.props;
history.push("/user/" + user.name);
history.push('/user/' + user.name);
};
createUser = (user: User) => {
this.props.addUser(this.props.usersLink, user, () =>
this.userCreated(user)
this.userCreated(user),
);
};
@@ -49,8 +48,8 @@ class CreateUser extends React.Component<Props> {
return (
<Page
title={t("createUser.title")}
subtitle={t("createUser.subtitle")}
title={t('createUser.title')}
subtitle={t('createUser.subtitle')}
error={error}
showContentOnError={true}
>
@@ -70,7 +69,7 @@ const mapDispatchToProps = dispatch => {
},
resetForm: () => {
dispatch(createUserReset());
}
},
};
};
@@ -81,11 +80,11 @@ const mapStateToProps = (state, ownProps) => {
return {
usersLink,
loading,
error
error,
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(translate("users")(CreateUser));
mapDispatchToProps,
)(translate('users')(CreateUser));

View File

@@ -1,41 +1,40 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import type { User } from "@scm-manager/ui-types";
import React from 'react';
import { translate } from 'react-i18next';
import { User } from '@scm-manager/ui-types';
import {
Subtitle,
DeleteButton,
confirmAlert,
ErrorNotification
} from "@scm-manager/ui-components";
ErrorNotification,
} from '@scm-manager/ui-components';
import {
deleteUser,
getDeleteUserFailure,
isDeleteUserPending
} from "../modules/users";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import type { History } from "history";
isDeleteUserPending,
} from '../modules/users';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { History } from 'history';
type Props = {
loading: boolean,
error: Error,
user: User,
confirmDialog?: boolean,
deleteUser: (user: User, callback?: () => void) => void,
loading: boolean;
error: Error;
user: User;
confirmDialog?: boolean;
deleteUser: (user: User, callback?: () => void) => void;
// context props
history: History,
t: string => string
history: History;
t: (p: string) => string;
};
class DeleteUser extends React.Component<Props> {
static defaultProps = {
confirmDialog: true
confirmDialog: true,
};
userDeleted = () => {
this.props.history.push("/users/");
this.props.history.push('/users/');
};
deleteUser = () => {
@@ -45,18 +44,18 @@ class DeleteUser extends React.Component<Props> {
confirmDelete = () => {
const { t } = this.props;
confirmAlert({
title: t("deleteUser.confirmAlert.title"),
message: t("deleteUser.confirmAlert.message"),
title: t('deleteUser.confirmAlert.title'),
message: t('deleteUser.confirmAlert.message'),
buttons: [
{
label: t("deleteUser.confirmAlert.submit"),
onClick: () => this.deleteUser()
label: t('deleteUser.confirmAlert.submit'),
onClick: () => this.deleteUser(),
},
{
label: t("deleteUser.confirmAlert.cancel"),
onClick: () => null
}
]
label: t('deleteUser.confirmAlert.cancel'),
onClick: () => null,
},
],
});
};
@@ -74,12 +73,12 @@ class DeleteUser extends React.Component<Props> {
return (
<>
<Subtitle subtitle={t("deleteUser.subtitle")} />
<Subtitle subtitle={t('deleteUser.subtitle')} />
<ErrorNotification error={error} />
<div className="columns">
<div className="column">
<DeleteButton
label={t("deleteUser.button")}
label={t('deleteUser.button')}
action={action}
loading={loading}
/>
@@ -95,7 +94,7 @@ const mapStateToProps = (state, ownProps) => {
const error = getDeleteUserFailure(state, ownProps.user.name);
return {
loading,
error
error,
};
};
@@ -103,11 +102,11 @@ const mapDispatchToProps = dispatch => {
return {
deleteUser: (user: User, callback?: () => void) => {
dispatch(deleteUser(user, callback));
}
},
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(translate("users")(DeleteUser)));
mapDispatchToProps,
)(withRouter(translate('users')(DeleteUser)));

View File

@@ -1,30 +1,29 @@
//@flow
import React from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import UserForm from "../components/UserForm";
import DeleteUser from "./DeleteUser";
import type { User } from "@scm-manager/ui-types";
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import UserForm from '../components/UserForm';
import DeleteUser from './DeleteUser';
import { User } from '@scm-manager/ui-types';
import {
modifyUser,
isModifyUserPending,
getModifyUserFailure,
modifyUserReset
} from "../modules/users";
import type { History } from "history";
import { ErrorNotification } from "@scm-manager/ui-components";
modifyUserReset,
} from '../modules/users';
import { History } from 'history';
import { ErrorNotification } from '@scm-manager/ui-components';
type Props = {
loading: boolean,
error: Error,
loading: boolean;
error: Error;
// dispatch functions
modifyUser: (user: User, callback?: () => void) => void,
modifyUserReset: User => void,
modifyUser: (user: User, callback?: () => void) => void;
modifyUserReset: (p: User) => void;
// context objects
user: User,
history: History
user: User;
history: History;
};
class EditUser extends React.Component<Props> {
@@ -63,7 +62,7 @@ const mapStateToProps = (state, ownProps) => {
const error = getModifyUserFailure(state, ownProps.user.name);
return {
loading,
error
error,
};
};
@@ -74,11 +73,11 @@ const mapDispatchToProps = dispatch => {
},
modifyUserReset: (user: User) => {
dispatch(modifyUserReset(user));
}
},
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
mapDispatchToProps,
)(withRouter(EditUser));

View File

@@ -1,6 +1,5 @@
//@flow
import React from "react";
import { connect } from "react-redux";
import React from 'react';
import { connect } from 'react-redux';
import {
Page,
Loading,
@@ -8,40 +7,44 @@ import {
SubNavigation,
Section,
NavLink,
ErrorPage
} from "@scm-manager/ui-components";
import { Route } from "react-router-dom";
import { Details } from "./../components/table";
import EditUser from "./EditUser";
import type { User } from "@scm-manager/ui-types";
import type { History } from "history";
ErrorPage,
} from '@scm-manager/ui-components';
import { Route } from 'react-router-dom';
import { Details } from './../components/table';
import EditUser from './EditUser';
import { User } from '@scm-manager/ui-types';
import { History } from 'history';
import {
fetchUserByName,
getUserByName,
isFetchUserPending,
getFetchUserFailure
} from "../modules/users";
import { EditUserNavLink, SetPasswordNavLink, SetPermissionsNavLink } from "./../components/navLinks";
import { translate } from "react-i18next";
import { getUsersLink } from "../../modules/indexResource";
import SetUserPassword from "../components/SetUserPassword";
import SetPermissions from "../../permissions/components/SetPermissions";
import {ExtensionPoint} from "@scm-manager/ui-extensions";
getFetchUserFailure,
} from '../modules/users';
import {
EditUserNavLink,
SetPasswordNavLink,
SetPermissionsNavLink,
} from './../components/navLinks';
import { translate } from 'react-i18next';
import { getUsersLink } from '../../modules/indexResource';
import SetUserPassword from '../components/SetUserPassword';
import SetPermissions from '../../permissions/components/SetPermissions';
import { ExtensionPoint } from '@scm-manager/ui-extensions';
type Props = {
name: string,
user: User,
loading: boolean,
error: Error,
usersLink: string,
name: string;
user: User;
loading: boolean;
error: Error;
usersLink: string;
// dispatcher function
fetchUserByName: (string, string) => void,
fetchUserByName: (p1: string, p2: string) => void;
// context objects
t: string => string,
match: any,
history: History
t: (p: string) => string;
match: any;
history: History;
};
class SingleUser extends React.Component<Props> {
@@ -50,7 +53,7 @@ class SingleUser extends React.Component<Props> {
}
stripEndingSlash = (url: string) => {
if (url.endsWith("/")) {
if (url.endsWith('/')) {
return url.substring(0, url.length - 2);
}
return url;
@@ -66,8 +69,8 @@ class SingleUser extends React.Component<Props> {
if (error) {
return (
<ErrorPage
title={t("singleUser.errorTitle")}
subtitle={t("singleUser.errorSubtitle")}
title={t('singleUser.errorTitle')}
subtitle={t('singleUser.errorSubtitle')}
error={error}
/>
);
@@ -81,7 +84,7 @@ class SingleUser extends React.Component<Props> {
const extensionProps = {
user,
url
url,
};
return (
@@ -113,15 +116,15 @@ class SingleUser extends React.Component<Props> {
</div>
<div className="column">
<Navigation>
<Section label={t("singleUser.menu.navigationLabel")}>
<Section label={t('singleUser.menu.navigationLabel')}>
<NavLink
to={`${url}`}
icon="fas fa-info-circle"
label={t("singleUser.menu.informationNavLink")}
label={t('singleUser.menu.informationNavLink')}
/>
<SubNavigation
to={`${url}/settings/general`}
label={t("singleUser.menu.settingsNavLink")}
label={t('singleUser.menu.settingsNavLink')}
>
<EditUserNavLink
user={user}
@@ -161,7 +164,7 @@ const mapStateToProps = (state, ownProps) => {
name,
user,
loading,
error
error,
};
};
@@ -169,11 +172,11 @@ const mapDispatchToProps = dispatch => {
return {
fetchUserByName: (link: string, name: string) => {
dispatch(fetchUserByName(link, name));
}
},
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(translate("users")(SingleUser));
mapDispatchToProps,
)(translate('users')(SingleUser));

View File

@@ -1,17 +1,16 @@
// @flow
import React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import type { History } from "history";
import type { User, PagedCollection } from "@scm-manager/ui-types";
import React from 'react';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { History } from 'history';
import { User, PagedCollection } from '@scm-manager/ui-types';
import {
fetchUsersByPage,
getUsersFromState,
selectListAsCollection,
isPermittedToCreateUsers,
isFetchUsersPending,
getFetchUsersFailure
} from "../modules/users";
getFetchUsersFailure,
} from '../modules/users';
import {
Page,
PageActions,
@@ -19,27 +18,27 @@ import {
Notification,
LinkPaginator,
urls,
CreateButton
} from "@scm-manager/ui-components";
import { UserTable } from "./../components/table";
import { getUsersLink } from "../../modules/indexResource";
CreateButton,
} from '@scm-manager/ui-components';
import { UserTable } from './../components/table';
import { getUsersLink } from '../../modules/indexResource';
type Props = {
users: User[],
loading: boolean,
error: Error,
canAddUsers: boolean,
list: PagedCollection,
page: number,
usersLink: string,
users: User[];
loading: boolean;
error: Error;
canAddUsers: boolean;
list: PagedCollection;
page: number;
usersLink: string;
// context objects
t: string => string,
history: History,
location: any,
t: (p: string) => string;
history: History;
location: any;
// dispatch functions
fetchUsersByPage: (link: string, page: number, filter?: string) => void
fetchUsersByPage: (link: string, page: number, filter?: string) => void;
};
class Users extends React.Component<Props> {
@@ -48,7 +47,7 @@ class Users extends React.Component<Props> {
fetchUsersByPage(
usersLink,
page,
urls.getQueryStringFromLocation(location)
urls.getQueryStringFromLocation(location),
);
}
@@ -59,7 +58,7 @@ class Users extends React.Component<Props> {
page,
usersLink,
location,
fetchUsersByPage
fetchUsersByPage,
} = this.props;
if (list && page && !loading) {
const statePage: number = list.page + 1;
@@ -67,7 +66,7 @@ class Users extends React.Component<Props> {
fetchUsersByPage(
usersLink,
page,
urls.getQueryStringFromLocation(location)
urls.getQueryStringFromLocation(location),
);
}
}
@@ -77,8 +76,8 @@ class Users extends React.Component<Props> {
const { users, loading, error, canAddUsers, t } = this.props;
return (
<Page
title={t("users.title")}
subtitle={t("users.subtitle")}
title={t('users.title')}
subtitle={t('users.subtitle')}
loading={loading || !users}
error={error}
>
@@ -88,7 +87,7 @@ class Users extends React.Component<Props> {
<OverviewPageActions
showCreateButton={canAddUsers}
link="users"
label={t("users.createButton")}
label={t('users.createButton')}
/>
</PageActions>
</Page>
@@ -109,14 +108,14 @@ class Users extends React.Component<Props> {
</>
);
}
return <Notification type="info">{t("users.noUsers")}</Notification>;
return <Notification type="info">{t('users.noUsers')}</Notification>;
}
renderCreateButton() {
const { canAddUsers, t } = this.props;
if (canAddUsers) {
return (
<CreateButton label={t("users.createButton")} link="/users/create" />
<CreateButton label={t('users.createButton')} link="/users/create" />
);
}
return null;
@@ -140,7 +139,7 @@ const mapStateToProps = (state, ownProps) => {
canAddUsers,
list,
page,
usersLink
usersLink,
};
};
@@ -148,11 +147,11 @@ const mapDispatchToProps = dispatch => {
return {
fetchUsersByPage: (link: string, page: number, filter?: string) => {
dispatch(fetchUsersByPage(link, page, filter));
}
},
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(translate("users")(Users));
mapDispatchToProps,
)(translate('users')(Users));

View File

@@ -1,7 +1,6 @@
//@flow
import configureMockStore from "redux-mock-store";
import thunk from "redux-thunk";
import fetchMock from "fetch-mock";
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import fetchMock from 'fetch-mock';
import reducer, {
FETCH_USERS,
@@ -46,53 +45,53 @@ import reducer, {
deleteUserSuccess,
getDeleteUserFailure,
selectListAsCollection,
isPermittedToCreateUsers
} from "./users";
isPermittedToCreateUsers,
} from './users';
const userZaphod = {
active: true,
admin: true,
creationDate: "2018-07-11T12:23:49.027Z",
displayName: "Z. Beeblebrox",
mail: "president@heartofgold.universe",
name: "zaphod",
password: "",
type: "xml",
creationDate: '2018-07-11T12:23:49.027Z',
displayName: 'Z. Beeblebrox',
mail: 'president@heartofgold.universe',
name: 'zaphod',
password: '',
type: 'xml',
properties: {},
_links: {
self: {
href: "http://localhost:8081/api/v2/users/zaphod"
href: 'http://localhost:8081/api/v2/users/zaphod',
},
delete: {
href: "http://localhost:8081/api/v2/users/zaphod"
href: 'http://localhost:8081/api/v2/users/zaphod',
},
update: {
href: "http://localhost:8081/api/v2/users/zaphod"
}
}
href: 'http://localhost:8081/api/v2/users/zaphod',
},
},
};
const userFord = {
active: true,
admin: false,
creationDate: "2018-07-06T13:21:18.459Z",
displayName: "F. Prefect",
mail: "ford@prefect.universe",
name: "ford",
password: "",
type: "xml",
creationDate: '2018-07-06T13:21:18.459Z',
displayName: 'F. Prefect',
mail: 'ford@prefect.universe',
name: 'ford',
password: '',
type: 'xml',
properties: {},
_links: {
self: {
href: "http://localhost:8081/api/v2/users/ford"
href: 'http://localhost:8081/api/v2/users/ford',
},
delete: {
href: "http://localhost:8081/api/v2/users/ford"
href: 'http://localhost:8081/api/v2/users/ford',
},
update: {
href: "http://localhost:8081/api/v2/users/ford"
}
}
href: 'http://localhost:8081/api/v2/users/ford',
},
},
};
const responseBody = {
@@ -100,50 +99,54 @@ const responseBody = {
pageTotal: 1,
_links: {
self: {
href: "http://localhost:3000/api/v2/users/?page=0&pageSize=10"
href: 'http://localhost:3000/api/v2/users/?page=0&pageSize=10',
},
first: {
href: "http://localhost:3000/api/v2/users/?page=0&pageSize=10"
href: 'http://localhost:3000/api/v2/users/?page=0&pageSize=10',
},
last: {
href: "http://localhost:3000/api/v2/users/?page=0&pageSize=10"
href: 'http://localhost:3000/api/v2/users/?page=0&pageSize=10',
},
create: {
href: "http://localhost:3000/api/v2/users/"
}
href: 'http://localhost:3000/api/v2/users/',
},
},
_embedded: {
users: [userZaphod, userFord]
}
users: [userZaphod, userFord],
},
};
const response = {
headers: { "content-type": "application/json" },
responseBody
headers: {
'content-type': 'application/json',
},
responseBody,
};
const URL = "users";
const USERS_URL = "/api/v2/users";
const USER_ZAPHOD_URL = "http://localhost:8081/api/v2/users/zaphod";
const URL = 'users';
const USERS_URL = '/api/v2/users';
const USER_ZAPHOD_URL = 'http://localhost:8081/api/v2/users/zaphod';
const error = new Error("KAPUTT");
const error = new Error('KAPUTT');
describe("users fetch()", () => {
describe('users fetch()', () => {
const mockStore = configureMockStore([thunk]);
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it("should successfully fetch users", () => {
it('should successfully fetch users', () => {
fetchMock.getOnce(USERS_URL, response);
const expectedActions = [
{ type: FETCH_USERS_PENDING },
{
type: FETCH_USERS_PENDING,
},
{
type: FETCH_USERS_SUCCESS,
payload: response
}
payload: response,
},
];
const store = mockStore({});
@@ -153,9 +156,9 @@ describe("users fetch()", () => {
});
});
it("should fail getting users on HTTP 500", () => {
it('should fail getting users on HTTP 500', () => {
fetchMock.getOnce(USERS_URL, {
status: 500
status: 500,
});
const store = mockStore({});
@@ -167,11 +170,11 @@ describe("users fetch()", () => {
});
});
it("should sucessfully fetch single user by name", () => {
fetchMock.getOnce(USERS_URL + "/zaphod", userZaphod);
it('should sucessfully fetch single user by name', () => {
fetchMock.getOnce(USERS_URL + '/zaphod', userZaphod);
const store = mockStore({});
return store.dispatch(fetchUserByName(URL, "zaphod")).then(() => {
return store.dispatch(fetchUserByName(URL, 'zaphod')).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_USER_PENDING);
expect(actions[1].type).toEqual(FETCH_USER_SUCCESS);
@@ -179,13 +182,13 @@ describe("users fetch()", () => {
});
});
it("should fail fetching single user by name on HTTP 500", () => {
fetchMock.getOnce(USERS_URL + "/zaphod", {
status: 500
it('should fail fetching single user by name on HTTP 500', () => {
fetchMock.getOnce(USERS_URL + '/zaphod', {
status: 500,
});
const store = mockStore({});
return store.dispatch(fetchUserByName(URL, "zaphod")).then(() => {
return store.dispatch(fetchUserByName(URL, 'zaphod')).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_USER_PENDING);
expect(actions[1].type).toEqual(FETCH_USER_FAILURE);
@@ -193,7 +196,7 @@ describe("users fetch()", () => {
});
});
it("should sucessfully fetch single user", () => {
it('should sucessfully fetch single user', () => {
fetchMock.getOnce(USER_ZAPHOD_URL, userZaphod);
const store = mockStore({});
@@ -205,9 +208,9 @@ describe("users fetch()", () => {
});
});
it("should fail fetching single user on HTTP 500", () => {
it('should fail fetching single user on HTTP 500', () => {
fetchMock.getOnce(USER_ZAPHOD_URL, {
status: 500
status: 500,
});
const store = mockStore({});
@@ -219,10 +222,10 @@ describe("users fetch()", () => {
});
});
it("should add a user successfully", () => {
it('should add a user successfully', () => {
// unmatched
fetchMock.postOnce(USERS_URL, {
status: 204
status: 204,
});
// after create, the users are fetched again
@@ -236,9 +239,9 @@ describe("users fetch()", () => {
});
});
it("should fail adding a user on HTTP 500", () => {
it('should fail adding a user on HTTP 500', () => {
fetchMock.postOnce(USERS_URL, {
status: 500
status: 500,
});
const store = mockStore({});
@@ -250,27 +253,27 @@ describe("users fetch()", () => {
});
});
it("should call the callback after user successfully created", () => {
it('should call the callback after user successfully created', () => {
// unmatched
fetchMock.postOnce(USERS_URL, {
status: 204
status: 204,
});
let callMe = "not yet";
let callMe = 'not yet';
const callback = () => {
callMe = "yeah";
callMe = 'yeah';
};
const store = mockStore({});
return store.dispatch(createUser(URL, userZaphod, callback)).then(() => {
expect(callMe).toBe("yeah");
expect(callMe).toBe('yeah');
});
});
it("successfully update user", () => {
it('successfully update user', () => {
fetchMock.putOnce(USER_ZAPHOD_URL, {
status: 204
status: 204,
});
fetchMock.getOnce(USER_ZAPHOD_URL, userZaphod);
@@ -284,9 +287,9 @@ describe("users fetch()", () => {
});
});
it("should call callback, after successful modified user", () => {
it('should call callback, after successful modified user', () => {
fetchMock.putOnce(USER_ZAPHOD_URL, {
status: 204
status: 204,
});
fetchMock.getOnce(USER_ZAPHOD_URL, userZaphod);
@@ -301,9 +304,9 @@ describe("users fetch()", () => {
});
});
it("should fail updating user on HTTP 500", () => {
it('should fail updating user on HTTP 500', () => {
fetchMock.putOnce(USER_ZAPHOD_URL, {
status: 500
status: 500,
});
const store = mockStore({});
@@ -315,9 +318,9 @@ describe("users fetch()", () => {
});
});
it("should delete successfully user zaphod", () => {
it('should delete successfully user zaphod', () => {
fetchMock.deleteOnce(USER_ZAPHOD_URL, {
status: 204
status: 204,
});
const store = mockStore({});
@@ -330,9 +333,9 @@ describe("users fetch()", () => {
});
});
it("should call the callback, after successful delete", () => {
it('should call the callback, after successful delete', () => {
fetchMock.deleteOnce(USER_ZAPHOD_URL, {
status: 204
status: 204,
});
let called = false;
@@ -346,9 +349,9 @@ describe("users fetch()", () => {
});
});
it("should fail to delete user zaphod", () => {
it('should fail to delete user zaphod', () => {
fetchMock.deleteOnce(USER_ZAPHOD_URL, {
status: 500
status: 500,
});
const store = mockStore({});
@@ -362,296 +365,325 @@ describe("users fetch()", () => {
});
});
describe("users reducer", () => {
it("should update state correctly according to FETCH_USERS_SUCCESS action", () => {
describe('users reducer', () => {
it('should update state correctly according to FETCH_USERS_SUCCESS action', () => {
const newState = reducer({}, fetchUsersSuccess(responseBody));
expect(newState.list).toEqual({
entries: ["zaphod", "ford"],
entries: ['zaphod', 'ford'],
entry: {
userCreatePermission: true,
page: 0,
pageTotal: 1,
_links: responseBody._links
}
_links: responseBody._links,
},
});
expect(newState.byNames).toEqual({
zaphod: userZaphod,
ford: userFord
ford: userFord,
});
expect(newState.list.entry.userCreatePermission).toBeTruthy();
});
it("should set userCreatePermission to true if update link is present", () => {
it('should set userCreatePermission to true if update link is present', () => {
const newState = reducer({}, fetchUsersSuccess(responseBody));
expect(newState.list.entry.userCreatePermission).toBeTruthy();
});
it("should not replace whole byNames map when fetching users", () => {
it('should not replace whole byNames map when fetching users', () => {
const oldState = {
byNames: {
ford: userFord
}
ford: userFord,
},
};
const newState = reducer(oldState, fetchUsersSuccess(responseBody));
expect(newState.byNames["zaphod"]).toBeDefined();
expect(newState.byNames["ford"]).toBeDefined();
expect(newState.byNames['zaphod']).toBeDefined();
expect(newState.byNames['ford']).toBeDefined();
});
it("should remove user from state when delete succeeds", () => {
it('should remove user from state when delete succeeds', () => {
const state = {
list: {
entries: ["ford", "zaphod"]
entries: ['ford', 'zaphod'],
},
byNames: {
zaphod: userZaphod,
ford: userFord
}
ford: userFord,
},
};
const newState = reducer(state, deleteUserSuccess(userFord));
expect(newState.byNames["zaphod"]).toBeDefined();
expect(newState.byNames["ford"]).toBeFalsy();
expect(newState.list.entries).toEqual(["zaphod"]);
expect(newState.byNames['zaphod']).toBeDefined();
expect(newState.byNames['ford']).toBeFalsy();
expect(newState.list.entries).toEqual(['zaphod']);
});
it("should set userCreatePermission to true if create link is present", () => {
it('should set userCreatePermission to true if create link is present', () => {
const newState = reducer({}, fetchUsersSuccess(responseBody));
expect(newState.list.entry.userCreatePermission).toBeTruthy();
expect(newState.list.entries).toEqual(["zaphod", "ford"]);
expect(newState.byNames["ford"]).toBeTruthy();
expect(newState.byNames["zaphod"]).toBeTruthy();
expect(newState.list.entries).toEqual(['zaphod', 'ford']);
expect(newState.byNames['ford']).toBeTruthy();
expect(newState.byNames['zaphod']).toBeTruthy();
});
it("should update state according to FETCH_USER_SUCCESS action", () => {
it('should update state according to FETCH_USER_SUCCESS action', () => {
const newState = reducer({}, fetchUserSuccess(userFord));
expect(newState.byNames["ford"]).toBe(userFord);
expect(newState.byNames['ford']).toBe(userFord);
});
it("should affect users state nor the state of other users", () => {
it('should affect users state nor the state of other users', () => {
const newState = reducer(
{
list: {
entries: ["zaphod"]
}
entries: ['zaphod'],
},
},
fetchUserSuccess(userFord)
fetchUserSuccess(userFord),
);
expect(newState.byNames["ford"]).toBe(userFord);
expect(newState.list.entries).toEqual(["zaphod"]);
expect(newState.byNames['ford']).toBe(userFord);
expect(newState.list.entries).toEqual(['zaphod']);
});
});
describe("selector tests", () => {
it("should return an empty object", () => {
describe('selector tests', () => {
it('should return an empty object', () => {
expect(selectListAsCollection({})).toEqual({});
expect(selectListAsCollection({ users: { a: "a" } })).toEqual({});
expect(
selectListAsCollection({
users: {
a: 'a',
},
}),
).toEqual({});
});
it("should return a state slice collection", () => {
it('should return a state slice collection', () => {
const collection = {
page: 3,
totalPages: 42
totalPages: 42,
};
const state = {
users: {
list: {
entry: collection
}
}
entry: collection,
},
},
};
expect(selectListAsCollection(state)).toBe(collection);
});
it("should return false", () => {
it('should return false', () => {
expect(isPermittedToCreateUsers({})).toBe(false);
expect(isPermittedToCreateUsers({ users: { list: { entry: {} } } })).toBe(
false
);
expect(
isPermittedToCreateUsers({
users: { list: { entry: { userCreatePermission: false } } }
})
users: {
list: {
entry: {},
},
},
}),
).toBe(false);
expect(
isPermittedToCreateUsers({
users: {
list: {
entry: {
userCreatePermission: false,
},
},
},
}),
).toBe(false);
});
it("should return true", () => {
it('should return true', () => {
const state = {
users: {
list: {
entry: {
userCreatePermission: true
}
}
}
userCreatePermission: true,
},
},
},
};
expect(isPermittedToCreateUsers(state)).toBe(true);
});
it("should get users from state", () => {
it('should get users from state', () => {
const state = {
users: {
list: {
entries: ["a", "b"]
entries: ['a', 'b'],
},
byNames: {
a: { name: "a" },
b: { name: "b" }
}
}
a: {
name: 'a',
},
b: {
name: 'b',
},
},
},
};
expect(getUsersFromState(state)).toEqual([{ name: "a" }, { name: "b" }]);
expect(getUsersFromState(state)).toEqual([
{
name: 'a',
},
{
name: 'b',
},
]);
});
it("should return true, when fetch users is pending", () => {
it('should return true, when fetch users is pending', () => {
const state = {
pending: {
[FETCH_USERS]: true
}
[FETCH_USERS]: true,
},
};
expect(isFetchUsersPending(state)).toEqual(true);
});
it("should return false, when fetch users is not pending", () => {
it('should return false, when fetch users is not pending', () => {
expect(isFetchUsersPending({})).toEqual(false);
});
it("should return error when fetch users did fail", () => {
it('should return error when fetch users did fail', () => {
const state = {
failure: {
[FETCH_USERS]: error
}
[FETCH_USERS]: error,
},
};
expect(getFetchUsersFailure(state)).toEqual(error);
});
it("should return undefined when fetch users did not fail", () => {
it('should return undefined when fetch users did not fail', () => {
expect(getFetchUsersFailure({})).toBe(undefined);
});
it("should return true if create user is pending", () => {
it('should return true if create user is pending', () => {
const state = {
pending: {
[CREATE_USER]: true
}
[CREATE_USER]: true,
},
};
expect(isCreateUserPending(state)).toBe(true);
});
it("should return false if create user is not pending", () => {
it('should return false if create user is not pending', () => {
const state = {
pending: {
[CREATE_USER]: false
}
[CREATE_USER]: false,
},
};
expect(isCreateUserPending(state)).toBe(false);
});
it("should return error when create user did fail", () => {
it('should return error when create user did fail', () => {
const state = {
failure: {
[CREATE_USER]: error
}
[CREATE_USER]: error,
},
};
expect(getCreateUserFailure(state)).toEqual(error);
});
it("should return undefined when create user did not fail", () => {
it('should return undefined when create user did not fail', () => {
expect(getCreateUserFailure({})).toBe(undefined);
});
it("should return user ford", () => {
it('should return user ford', () => {
const state = {
users: {
byNames: {
ford: userFord
}
}
ford: userFord,
},
},
};
expect(getUserByName(state, "ford")).toEqual(userFord);
expect(getUserByName(state, 'ford')).toEqual(userFord);
});
it("should return true, when fetch user zaphod is pending", () => {
it('should return true, when fetch user zaphod is pending', () => {
const state = {
pending: {
[FETCH_USER + "/zaphod"]: true
}
[FETCH_USER + '/zaphod']: true,
},
};
expect(isFetchUserPending(state, "zaphod")).toEqual(true);
expect(isFetchUserPending(state, 'zaphod')).toEqual(true);
});
it("should return false, when fetch user zaphod is not pending", () => {
expect(isFetchUserPending({}, "zaphod")).toEqual(false);
it('should return false, when fetch user zaphod is not pending', () => {
expect(isFetchUserPending({}, 'zaphod')).toEqual(false);
});
it("should return error when fetch user zaphod did fail", () => {
it('should return error when fetch user zaphod did fail', () => {
const state = {
failure: {
[FETCH_USER + "/zaphod"]: error
}
[FETCH_USER + '/zaphod']: error,
},
};
expect(getFetchUserFailure(state, "zaphod")).toEqual(error);
expect(getFetchUserFailure(state, 'zaphod')).toEqual(error);
});
it("should return undefined when fetch user zaphod did not fail", () => {
expect(getFetchUserFailure({}, "zaphod")).toBe(undefined);
it('should return undefined when fetch user zaphod did not fail', () => {
expect(getFetchUserFailure({}, 'zaphod')).toBe(undefined);
});
it("should return true, when modify user ford is pending", () => {
it('should return true, when modify user ford is pending', () => {
const state = {
pending: {
[MODIFY_USER + "/ford"]: true
}
[MODIFY_USER + '/ford']: true,
},
};
expect(isModifyUserPending(state, "ford")).toEqual(true);
expect(isModifyUserPending(state, 'ford')).toEqual(true);
});
it("should return false, when modify user ford is not pending", () => {
expect(isModifyUserPending({}, "ford")).toEqual(false);
it('should return false, when modify user ford is not pending', () => {
expect(isModifyUserPending({}, 'ford')).toEqual(false);
});
it("should return error when modify user ford did fail", () => {
it('should return error when modify user ford did fail', () => {
const state = {
failure: {
[MODIFY_USER + "/ford"]: error
}
[MODIFY_USER + '/ford']: error,
},
};
expect(getModifyUserFailure(state, "ford")).toEqual(error);
expect(getModifyUserFailure(state, 'ford')).toEqual(error);
});
it("should return undefined when modify user ford did not fail", () => {
expect(getModifyUserFailure({}, "ford")).toBe(undefined);
it('should return undefined when modify user ford did not fail', () => {
expect(getModifyUserFailure({}, 'ford')).toBe(undefined);
});
it("should return true, when delete user zaphod is pending", () => {
it('should return true, when delete user zaphod is pending', () => {
const state = {
pending: {
[DELETE_USER + "/zaphod"]: true
}
[DELETE_USER + '/zaphod']: true,
},
};
expect(isDeleteUserPending(state, "zaphod")).toEqual(true);
expect(isDeleteUserPending(state, 'zaphod')).toEqual(true);
});
it("should return false, when delete user zaphod is not pending", () => {
expect(isDeleteUserPending({}, "zaphod")).toEqual(false);
it('should return false, when delete user zaphod is not pending', () => {
expect(isDeleteUserPending({}, 'zaphod')).toEqual(false);
});
it("should return error when delete user zaphod did fail", () => {
it('should return error when delete user zaphod did fail', () => {
const state = {
failure: {
[DELETE_USER + "/zaphod"]: error
}
[DELETE_USER + '/zaphod']: error,
},
};
expect(getDeleteUserFailure(state, "zaphod")).toEqual(error);
expect(getDeleteUserFailure(state, 'zaphod')).toEqual(error);
});
it("should return undefined when delete user zaphod did not fail", () => {
expect(getDeleteUserFailure({}, "zaphod")).toBe(undefined);
it('should return undefined when delete user zaphod did not fail', () => {
expect(getDeleteUserFailure({}, 'zaphod')).toBe(undefined);
});
});

View File

@@ -1,39 +1,38 @@
// @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 { User, Action, PagedCollection } from "@scm-manager/ui-types";
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 { User, Action, PagedCollection } from '@scm-manager/ui-types';
export const FETCH_USERS = "scm/users/FETCH_USERS";
export const FETCH_USERS = 'scm/users/FETCH_USERS';
export const FETCH_USERS_PENDING = `${FETCH_USERS}_${types.PENDING_SUFFIX}`;
export const FETCH_USERS_SUCCESS = `${FETCH_USERS}_${types.SUCCESS_SUFFIX}`;
export const FETCH_USERS_FAILURE = `${FETCH_USERS}_${types.FAILURE_SUFFIX}`;
export const FETCH_USER = "scm/users/FETCH_USER";
export const FETCH_USER = 'scm/users/FETCH_USER';
export const FETCH_USER_PENDING = `${FETCH_USER}_${types.PENDING_SUFFIX}`;
export const FETCH_USER_SUCCESS = `${FETCH_USER}_${types.SUCCESS_SUFFIX}`;
export const FETCH_USER_FAILURE = `${FETCH_USER}_${types.FAILURE_SUFFIX}`;
export const CREATE_USER = "scm/users/CREATE_USER";
export const CREATE_USER = 'scm/users/CREATE_USER';
export const CREATE_USER_PENDING = `${CREATE_USER}_${types.PENDING_SUFFIX}`;
export const CREATE_USER_SUCCESS = `${CREATE_USER}_${types.SUCCESS_SUFFIX}`;
export const CREATE_USER_FAILURE = `${CREATE_USER}_${types.FAILURE_SUFFIX}`;
export const CREATE_USER_RESET = `${CREATE_USER}_${types.RESET_SUFFIX}`;
export const MODIFY_USER = "scm/users/MODIFY_USER";
export const MODIFY_USER = 'scm/users/MODIFY_USER';
export const MODIFY_USER_PENDING = `${MODIFY_USER}_${types.PENDING_SUFFIX}`;
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_USER";
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}`;
const CONTENT_TYPE_USER = "application/vnd.scmm-user+json;v=2";
const CONTENT_TYPE_USER = 'application/vnd.scmm-user+json;v=2';
// TODO i18n for error messages
@@ -47,7 +46,7 @@ export function fetchUsersByPage(link: string, page: number, filter?: string) {
// backend start counting by 0
if (filter) {
return fetchUsersByLink(
`${link}?page=${page - 1}&q=${decodeURIComponent(filter)}`
`${link}?page=${page - 1}&q=${decodeURIComponent(filter)}`,
);
}
return fetchUsersByLink(`${link}?page=${page - 1}`);
@@ -70,14 +69,14 @@ export function fetchUsersByLink(link: string) {
export function fetchUsersPending(): Action {
return {
type: FETCH_USERS_PENDING
type: FETCH_USERS_PENDING,
};
}
export function fetchUsersSuccess(users: any): Action {
return {
type: FETCH_USERS_SUCCESS,
payload: users
payload: users,
};
}
@@ -86,14 +85,14 @@ export function fetchUsersFailure(url: string, error: Error): Action {
type: FETCH_USERS_FAILURE,
payload: {
error,
url
}
url,
},
};
}
//fetch user
export function fetchUserByName(link: string, name: string) {
const userUrl = link.endsWith("/") ? link + name : link + "/" + name;
const userUrl = link.endsWith('/') ? link + name : link + '/' + name;
return fetchUser(userUrl, name);
}
@@ -122,7 +121,7 @@ export function fetchUserPending(name: string): Action {
return {
type: FETCH_USER_PENDING,
payload: name,
itemId: name
itemId: name,
};
}
@@ -130,7 +129,7 @@ export function fetchUserSuccess(user: any): Action {
return {
type: FETCH_USER_SUCCESS,
payload: user,
itemId: user.name
itemId: user.name,
};
}
@@ -139,9 +138,9 @@ export function fetchUserFailure(name: string, error: Error): Action {
type: FETCH_USER_FAILURE,
payload: {
name,
error
error,
},
itemId: name
itemId: name,
};
}
@@ -165,26 +164,26 @@ export function createUser(link: string, user: User, callback?: () => void) {
export function createUserPending(user: User): Action {
return {
type: CREATE_USER_PENDING,
user
user,
};
}
export function createUserSuccess(): Action {
return {
type: CREATE_USER_SUCCESS
type: CREATE_USER_SUCCESS,
};
}
export function createUserFailure(error: Error): Action {
return {
type: CREATE_USER_FAILURE,
payload: error
payload: error,
};
}
export function createUserReset() {
return {
type: CREATE_USER_RESET
type: CREATE_USER_RESET,
};
}
@@ -214,7 +213,7 @@ export function modifyUserPending(user: User): Action {
return {
type: MODIFY_USER_PENDING,
payload: user,
itemId: user.name
itemId: user.name,
};
}
@@ -222,7 +221,7 @@ export function modifyUserSuccess(user: User): Action {
return {
type: MODIFY_USER_SUCCESS,
payload: user,
itemId: user.name
itemId: user.name,
};
}
@@ -231,16 +230,16 @@ export function modifyUserFailure(user: User, error: Error): Action {
type: MODIFY_USER_FAILURE,
payload: {
error,
user
user,
},
itemId: user.name
itemId: user.name,
};
}
export function modifyUserReset(user: User): Action {
return {
type: MODIFY_USER_RESET,
itemId: user.name
itemId: user.name,
};
}
@@ -267,7 +266,7 @@ export function deleteUserPending(user: User): Action {
return {
type: DELETE_USER_PENDING,
payload: user,
itemId: user.name
itemId: user.name,
};
}
@@ -275,7 +274,7 @@ export function deleteUserSuccess(user: User): Action {
return {
type: DELETE_USER_SUCCESS,
payload: user,
itemId: user.name
itemId: user.name,
};
}
@@ -284,16 +283,16 @@ export function deleteUserFailure(user: User, error: Error): Action {
type: DELETE_USER_FAILURE,
payload: {
error,
user
user,
},
itemId: user.name
itemId: user.name,
};
}
function extractUsersByNames(
users: User[],
userNames: string[],
oldUsersByNames: Object
oldUsersByNames: object,
) {
const usersByNames = {};
@@ -326,7 +325,7 @@ function deleteUserInEntries(users: [], userName: string) {
const reducerByName = (state: any, username: string, newUserState: any) => {
return {
...state,
[username]: newUserState
[username]: newUserState,
};
};
@@ -342,19 +341,19 @@ function listReducer(state: any = {}, action: any = {}) {
userCreatePermission: !!action.payload._links.create,
page: action.payload.page,
pageTotal: action.payload.pageTotal,
_links: action.payload._links
}
_links: action.payload._links,
},
};
// Delete single user actions
case DELETE_USER_SUCCESS:
const newUserEntries = deleteUserInEntries(
state.entries,
action.payload.name
action.payload.name,
);
return {
...state,
entries: newUserEntries
entries: newUserEntries,
};
default:
return state;
@@ -369,7 +368,7 @@ function byNamesReducer(state: any = {}, action: any = {}) {
const userNames = users.map(user => user.name);
const byNames = extractUsersByNames(users, userNames, state.byNames);
return {
...byNames
...byNames,
};
// Fetch single user actions
@@ -377,10 +376,7 @@ function byNamesReducer(state: any = {}, action: any = {}) {
return reducerByName(state, action.payload.name, action.payload);
case DELETE_USER_SUCCESS:
return deleteUserInUsersByNames(
state,
action.payload.name
);
return deleteUserInUsersByNames(state, action.payload.name);
default:
return state;
@@ -389,19 +385,19 @@ function byNamesReducer(state: any = {}, action: any = {}) {
export default combineReducers({
list: listReducer,
byNames: byNamesReducer
byNames: byNamesReducer,
});
// selectors
const selectList = (state: Object) => {
const selectList = (state: object) => {
if (state.users && state.users.list) {
return state.users.list;
}
return {};
};
const selectListEntry = (state: Object): Object => {
const selectListEntry = (state: object): object => {
const list = selectList(state);
if (list.entry) {
return list.entry;
@@ -409,15 +405,15 @@ const selectListEntry = (state: Object): Object => {
return {};
};
export const selectListAsCollection = (state: Object): PagedCollection => {
export const selectListAsCollection = (state: object): PagedCollection => {
return selectListEntry(state);
};
export const isPermittedToCreateUsers = (state: Object): boolean => {
export const isPermittedToCreateUsers = (state: object): boolean => {
return !!selectListEntry(state).userCreatePermission;
};
export function getUsersFromState(state: Object) {
export function getUsersFromState(state: object) {
const userNames = selectList(state).entries;
if (!userNames) {
return null;
@@ -431,48 +427,48 @@ export function getUsersFromState(state: Object) {
return userEntries;
}
export function isFetchUsersPending(state: Object) {
export function isFetchUsersPending(state: object) {
return isPending(state, FETCH_USERS);
}
export function getFetchUsersFailure(state: Object) {
export function getFetchUsersFailure(state: object) {
return getFailure(state, FETCH_USERS);
}
export function isCreateUserPending(state: Object) {
export function isCreateUserPending(state: object) {
return isPending(state, CREATE_USER);
}
export function getCreateUserFailure(state: Object) {
export function getCreateUserFailure(state: object) {
return getFailure(state, CREATE_USER);
}
export function getUserByName(state: Object, name: string) {
export function getUserByName(state: object, name: string) {
if (state.users && state.users.byNames) {
return state.users.byNames[name];
}
}
export function isFetchUserPending(state: Object, name: string) {
export function isFetchUserPending(state: object, name: string) {
return isPending(state, FETCH_USER, name);
}
export function getFetchUserFailure(state: Object, name: string) {
export function getFetchUserFailure(state: object, name: string) {
return getFailure(state, FETCH_USER, name);
}
export function isModifyUserPending(state: Object, name: string) {
export function isModifyUserPending(state: object, name: string) {
return isPending(state, MODIFY_USER, name);
}
export function getModifyUserFailure(state: Object, name: string) {
export function getModifyUserFailure(state: object, name: string) {
return getFailure(state, MODIFY_USER, name);
}
export function isDeleteUserPending(state: Object, name: string) {
export function isDeleteUserPending(state: object, name: string) {
return isPending(state, DELETE_USER, name);
}
export function getDeleteUserFailure(state: Object, name: string) {
export function getDeleteUserFailure(state: object, name: string) {
return getFailure(state, DELETE_USER, name);
}