apply prettier, removed flow related config and added tsconfig

This commit is contained in:
Sebastian Sdorra
2019-10-20 18:02:52 +02:00
parent 0e017dcadd
commit 490418d06e
231 changed files with 5771 additions and 30386 deletions

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Checkbox, InputField, Subtitle } from '@scm-manager/ui-components';
import React from "react";
import { translate } from "react-i18next";
import { Checkbox, InputField, Subtitle } from "@scm-manager/ui-components";
type Props = {
baseUrl: string;
@@ -16,24 +16,24 @@ class BaseUrlSettings extends React.Component<Props> {
return (
<div>
<Subtitle subtitle={t('base-url-settings.name')} />
<Subtitle subtitle={t("base-url-settings.name")} />
<div className="columns">
<div className="column is-half">
<InputField
label={t('base-url-settings.base-url')}
label={t("base-url-settings.base-url")}
onChange={this.handleBaseUrlChange}
value={baseUrl}
disabled={!hasUpdatePermission}
helpText={t('help.baseUrlHelpText')}
helpText={t("help.baseUrlHelpText")}
/>
</div>
<div className="column is-half">
<Checkbox
checked={forceBaseUrl}
label={t('base-url-settings.force-base-url')}
label={t("base-url-settings.force-base-url")}
onChange={this.handleForceBaseUrlChange}
disabled={!hasUpdatePermission}
helpText={t('help.forceBaseUrlHelpText')}
helpText={t("help.forceBaseUrlHelpText")}
/>
</div>
</div>
@@ -42,11 +42,11 @@ class BaseUrlSettings extends React.Component<Props> {
}
handleBaseUrlChange = (value: string) => {
this.props.onChange(true, value, 'baseUrl');
this.props.onChange(true, value, "baseUrl");
};
handleForceBaseUrlChange = (value: boolean) => {
this.props.onChange(true, value, 'forceBaseUrl');
this.props.onChange(true, value, "forceBaseUrl");
};
}
export default translate('config')(BaseUrlSettings);
export default translate("config")(BaseUrlSettings);

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { translate } from 'react-i18next';
import { SubmitButton, Notification } from '@scm-manager/ui-components';
import { NamespaceStrategies, Config } from '@scm-manager/ui-types';
import ProxySettings from './ProxySettings';
import GeneralSettings from './GeneralSettings';
import BaseUrlSettings from './BaseUrlSettings';
import LoginAttempt from './LoginAttempt';
import React from "react";
import { translate } from "react-i18next";
import { SubmitButton, Notification } from "@scm-manager/ui-components";
import { NamespaceStrategies, Config } from "@scm-manager/ui-types";
import ProxySettings from "./ProxySettings";
import GeneralSettings from "./GeneralSettings";
import BaseUrlSettings from "./BaseUrlSettings";
import LoginAttempt from "./LoginAttempt";
type Props = {
submitForm: (p: Config) => void;
@@ -36,31 +36,31 @@ class ConfigForm extends React.Component<Props, State> {
config: {
proxyPassword: null,
proxyPort: 0,
proxyServer: '',
proxyServer: "",
proxyUser: null,
enableProxy: false,
realmDescription: '',
realmDescription: "",
disableGroupingGrid: false,
dateFormat: '',
dateFormat: "",
anonymousAccessEnabled: false,
baseUrl: '',
baseUrl: "",
forceBaseUrl: false,
loginAttemptLimit: 0,
proxyExcludes: [],
skipFailedAuthenticators: false,
pluginUrl: '',
pluginUrl: "",
loginAttemptLimitTimeout: 0,
enabledXsrfProtection: true,
namespaceStrategy: '',
loginInfoUrl: '',
_links: {},
namespaceStrategy: "",
loginInfoUrl: "",
_links: {}
},
showNotification: false,
error: {
loginAttemptLimitTimeout: false,
loginAttemptLimit: false,
loginAttemptLimit: false
},
changed: false,
changed: false
};
}
@@ -70,14 +70,14 @@ class ConfigForm extends React.Component<Props, State> {
this.setState({
...this.state,
config: {
...config,
},
...config
}
});
}
if (!configUpdatePermission) {
this.setState({
...this.state,
showNotification: true,
showNotification: true
});
}
}
@@ -85,7 +85,7 @@ class ConfigForm extends React.Component<Props, State> {
submit = (event: Event) => {
event.preventDefault();
this.setState({
changed: false,
changed: false
});
this.props.submitForm(this.state.config);
};
@@ -96,7 +96,7 @@ class ConfigForm extends React.Component<Props, State> {
t,
namespaceStrategies,
configReadPermission,
configUpdatePermission,
configUpdatePermission
} = this.props;
const config = this.state.config;
@@ -105,8 +105,8 @@ class ConfigForm extends React.Component<Props, State> {
if (!configReadPermission) {
return (
<Notification
type={'danger'}
children={t('config.form.no-read-permission-notification')}
type={"danger"}
children={t("config.form.no-read-permission-notification")}
/>
);
}
@@ -114,8 +114,8 @@ class ConfigForm extends React.Component<Props, State> {
if (this.state.showNotification) {
noPermissionNotification = (
<Notification
type={'info'}
children={t('config.form.no-write-permission-notification')}
type={"info"}
children={t("config.form.no-write-permission-notification")}
onClose={() => this.onClose()}
/>
);
@@ -160,10 +160,10 @@ class ConfigForm extends React.Component<Props, State> {
/>
<hr />
<ProxySettings
proxyPassword={config.proxyPassword ? config.proxyPassword : ''}
proxyPassword={config.proxyPassword ? config.proxyPassword : ""}
proxyPort={config.proxyPort}
proxyServer={config.proxyServer ? config.proxyServer : ''}
proxyUser={config.proxyUser ? config.proxyUser : ''}
proxyServer={config.proxyServer ? config.proxyServer : ""}
proxyUser={config.proxyUser ? config.proxyUser : ""}
enableProxy={config.enableProxy}
proxyExcludes={config.proxyExcludes}
onChange={(isValid, changedValue, name) =>
@@ -174,7 +174,7 @@ class ConfigForm extends React.Component<Props, State> {
<hr />
<SubmitButton
loading={loading}
label={t('config.form.submit')}
label={t("config.form.submit")}
disabled={
!configUpdatePermission || this.hasError() || !this.state.changed
}
@@ -188,13 +188,13 @@ class ConfigForm extends React.Component<Props, State> {
...this.state,
config: {
...this.state.config,
[name]: changedValue,
[name]: changedValue
},
error: {
...this.state.error,
[name]: !isValid,
[name]: !isValid
},
changed: true,
changed: true
});
};
@@ -208,9 +208,9 @@ class ConfigForm extends React.Component<Props, State> {
onClose = () => {
this.setState({
...this.state,
showNotification: false,
showNotification: false
});
};
}
export default translate('config')(ConfigForm);
export default translate("config")(ConfigForm);

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Checkbox, InputField } from '@scm-manager/ui-components';
import { NamespaceStrategies } from '@scm-manager/ui-types';
import NamespaceStrategySelect from './NamespaceStrategySelect';
import React from "react";
import { translate } from "react-i18next";
import { Checkbox, InputField } from "@scm-manager/ui-components";
import { NamespaceStrategies } from "@scm-manager/ui-types";
import NamespaceStrategySelect from "./NamespaceStrategySelect";
type Props = {
realmDescription: string;
@@ -32,7 +32,7 @@ class GeneralSettings extends React.Component<Props> {
anonymousAccessEnabled,
namespaceStrategy,
hasUpdatePermission,
namespaceStrategies,
namespaceStrategies
} = this.props;
return (
@@ -40,61 +40,61 @@ class GeneralSettings extends React.Component<Props> {
<div className="columns">
<div className="column is-half">
<InputField
label={t('general-settings.realm-description')}
label={t("general-settings.realm-description")}
onChange={this.handleRealmDescriptionChange}
value={realmDescription}
disabled={!hasUpdatePermission}
helpText={t('help.realmDescriptionHelpText')}
helpText={t("help.realmDescriptionHelpText")}
/>
</div>
<div className="column is-half">
<NamespaceStrategySelect
label={t('general-settings.namespace-strategy')}
label={t("general-settings.namespace-strategy")}
onChange={this.handleNamespaceStrategyChange}
value={namespaceStrategy}
disabled={!hasUpdatePermission}
namespaceStrategies={namespaceStrategies}
helpText={t('help.nameSpaceStrategyHelpText')}
helpText={t("help.nameSpaceStrategyHelpText")}
/>
</div>
</div>
<div className="columns">
<div className="column is-half">
<InputField
label={t('general-settings.login-info-url')}
label={t("general-settings.login-info-url")}
onChange={this.handleLoginInfoUrlChange}
value={loginInfoUrl}
disabled={!hasUpdatePermission}
helpText={t('help.loginInfoUrlHelpText')}
helpText={t("help.loginInfoUrlHelpText")}
/>
</div>
<div className="column is-half">
<Checkbox
checked={enabledXsrfProtection}
label={t('general-settings.enabled-xsrf-protection')}
label={t("general-settings.enabled-xsrf-protection")}
onChange={this.handleEnabledXsrfProtectionChange}
disabled={!hasUpdatePermission}
helpText={t('help.enableXsrfProtectionHelpText')}
helpText={t("help.enableXsrfProtectionHelpText")}
/>
</div>
</div>
<div className="columns">
<div className="column is-half">
<InputField
label={t('general-settings.plugin-url')}
label={t("general-settings.plugin-url")}
onChange={this.handlePluginCenterUrlChange}
value={pluginUrl}
disabled={!hasUpdatePermission}
helpText={t('help.pluginUrlHelpText')}
helpText={t("help.pluginUrlHelpText")}
/>
</div>
<div className="column is-half">
<Checkbox
checked={anonymousAccessEnabled}
label={t('general-settings.anonymous-access-enabled')}
label={t("general-settings.anonymous-access-enabled")}
onChange={this.handleEnableAnonymousAccess}
disabled={!hasUpdatePermission}
helpText={t('help.allowAnonymousAccessHelpText')}
helpText={t("help.allowAnonymousAccessHelpText")}
/>
</div>
</div>
@@ -103,23 +103,23 @@ class GeneralSettings extends React.Component<Props> {
}
handleLoginInfoUrlChange = (value: string) => {
this.props.onChange(true, value, 'loginInfoUrl');
this.props.onChange(true, value, "loginInfoUrl");
};
handleRealmDescriptionChange = (value: string) => {
this.props.onChange(true, value, 'realmDescription');
this.props.onChange(true, value, "realmDescription");
};
handleEnabledXsrfProtectionChange = (value: boolean) => {
this.props.onChange(true, value, 'enabledXsrfProtection');
this.props.onChange(true, value, "enabledXsrfProtection");
};
handleEnableAnonymousAccess = (value: boolean) => {
this.props.onChange(true, value, 'anonymousAccessEnabled');
this.props.onChange(true, value, "anonymousAccessEnabled");
};
handleNamespaceStrategyChange = (value: string) => {
this.props.onChange(true, value, 'namespaceStrategy');
this.props.onChange(true, value, "namespaceStrategy");
};
handlePluginCenterUrlChange = (value: string) => {
this.props.onChange(true, value, 'pluginUrl');
this.props.onChange(true, value, "pluginUrl");
};
}
export default translate('config')(GeneralSettings);
export default translate("config")(GeneralSettings);

View File

@@ -1,10 +1,10 @@
import React from 'react';
import { translate } from 'react-i18next';
import React from "react";
import { translate } from "react-i18next";
import {
InputField,
Subtitle,
validation as validator,
} from '@scm-manager/ui-components';
validation as validator
} from "@scm-manager/ui-components";
type Props = {
loginAttemptLimit: number;
@@ -25,7 +25,7 @@ class LoginAttempt extends React.Component<Props, State> {
this.state = {
loginAttemptLimitError: false,
loginAttemptLimitTimeoutError: false,
loginAttemptLimitTimeoutError: false
};
}
render() {
@@ -33,33 +33,33 @@ class LoginAttempt extends React.Component<Props, State> {
t,
loginAttemptLimit,
loginAttemptLimitTimeout,
hasUpdatePermission,
hasUpdatePermission
} = this.props;
return (
<div>
<Subtitle subtitle={t('login-attempt.name')} />
<Subtitle subtitle={t("login-attempt.name")} />
<div className="columns">
<div className="column is-half">
<InputField
label={t('login-attempt.login-attempt-limit')}
label={t("login-attempt.login-attempt-limit")}
onChange={this.handleLoginAttemptLimitChange}
value={loginAttemptLimit}
disabled={!hasUpdatePermission}
validationError={this.state.loginAttemptLimitError}
errorMessage={t('validation.login-attempt-limit-invalid')}
helpText={t('help.loginAttemptLimitHelpText')}
errorMessage={t("validation.login-attempt-limit-invalid")}
helpText={t("help.loginAttemptLimitHelpText")}
/>
</div>
<div className="column is-half">
<InputField
label={t('login-attempt.login-attempt-limit-timeout')}
label={t("login-attempt.login-attempt-limit-timeout")}
onChange={this.handleLoginAttemptLimitTimeoutChange}
value={loginAttemptLimitTimeout}
disabled={!hasUpdatePermission}
validationError={this.state.loginAttemptLimitTimeoutError}
errorMessage={t('validation.login-attempt-limit-timeout-invalid')}
helpText={t('help.loginAttemptLimitTimeoutHelpText')}
errorMessage={t("validation.login-attempt-limit-timeout-invalid")}
helpText={t("help.loginAttemptLimitTimeoutHelpText")}
/>
</div>
</div>
@@ -71,26 +71,26 @@ class LoginAttempt extends React.Component<Props, State> {
handleLoginAttemptLimitChange = (value: string) => {
this.setState({
...this.state,
loginAttemptLimitError: !validator.isNumberValid(value),
loginAttemptLimitError: !validator.isNumberValid(value)
});
this.props.onChange(
validator.isNumberValid(value),
value,
'loginAttemptLimit',
"loginAttemptLimit"
);
};
handleLoginAttemptLimitTimeoutChange = (value: string) => {
this.setState({
...this.state,
loginAttemptLimitTimeoutError: !validator.isNumberValid(value),
loginAttemptLimitTimeoutError: !validator.isNumberValid(value)
});
this.props.onChange(
validator.isNumberValid(value),
value,
'loginAttemptLimitTimeout',
"loginAttemptLimitTimeout"
);
};
}
export default translate('config')(LoginAttempt);
export default translate("config")(LoginAttempt);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { translate, TFunction } from 'react-i18next';
import { Select } from '@scm-manager/ui-components';
import { NamespaceStrategies } from '@scm-manager/ui-types';
import React from "react";
import { translate, TFunction } from "react-i18next";
import { Select } from "@scm-manager/ui-components";
import { NamespaceStrategies } from "@scm-manager/ui-types";
type Props = {
namespaceStrategies: NamespaceStrategies;
@@ -23,14 +23,14 @@ class NamespaceStrategySelect extends React.Component<Props> {
}
return available.map(ns => {
const key = 'namespaceStrategies.' + ns;
const key = "namespaceStrategies." + ns;
let label = t(key);
if (label === key) {
label = ns;
}
return {
value: ns,
label: label,
label: label
};
});
};
@@ -63,4 +63,4 @@ class NamespaceStrategySelect extends React.Component<Props> {
}
}
export default translate('plugins')(NamespaceStrategySelect);
export default translate("plugins")(NamespaceStrategySelect);

View File

@@ -1,12 +1,12 @@
import React from 'react';
import { translate } from 'react-i18next';
import React from "react";
import { translate } from "react-i18next";
import {
Checkbox,
InputField,
Subtitle,
AddEntryToTableField,
} from '@scm-manager/ui-components';
import ProxyExcludesTable from '../table/ProxyExcludesTable';
AddEntryToTableField
} from "@scm-manager/ui-components";
import ProxyExcludesTable from "../table/ProxyExcludesTable";
type Props = {
proxyPassword: string;
@@ -30,61 +30,61 @@ class ProxySettings extends React.Component<Props> {
proxyUser,
enableProxy,
proxyExcludes,
hasUpdatePermission,
hasUpdatePermission
} = this.props;
return (
<div>
<Subtitle subtitle={t('proxy-settings.name')} />
<Subtitle subtitle={t("proxy-settings.name")} />
<div className="columns">
<div className="column is-full">
<Checkbox
checked={enableProxy}
label={t('proxy-settings.enable-proxy')}
label={t("proxy-settings.enable-proxy")}
onChange={this.handleEnableProxyChange}
disabled={!hasUpdatePermission}
helpText={t('help.enableProxyHelpText')}
helpText={t("help.enableProxyHelpText")}
/>
</div>
</div>
<div className="columns">
<div className="column is-half">
<InputField
label={t('proxy-settings.proxy-password')}
label={t("proxy-settings.proxy-password")}
onChange={this.handleProxyPasswordChange}
value={proxyPassword}
type="password"
disabled={!enableProxy || !hasUpdatePermission}
helpText={t('help.proxyPasswordHelpText')}
helpText={t("help.proxyPasswordHelpText")}
/>
</div>
<div className="column is-half">
<InputField
label={t('proxy-settings.proxy-port')}
label={t("proxy-settings.proxy-port")}
value={proxyPort}
onChange={this.handleProxyPortChange}
disabled={!enableProxy || !hasUpdatePermission}
helpText={t('help.proxyPortHelpText')}
helpText={t("help.proxyPortHelpText")}
/>
</div>
</div>
<div className="columns">
<div className="column is-half">
<InputField
label={t('proxy-settings.proxy-server')}
label={t("proxy-settings.proxy-server")}
value={proxyServer}
onChange={this.handleProxyServerChange}
disabled={!enableProxy || !hasUpdatePermission}
helpText={t('help.proxyServerHelpText')}
helpText={t("help.proxyServerHelpText")}
/>
</div>
<div className="column is-half">
<InputField
label={t('proxy-settings.proxy-user')}
label={t("proxy-settings.proxy-user")}
value={proxyUser}
onChange={this.handleProxyUserChange}
disabled={!enableProxy || !hasUpdatePermission}
helpText={t('help.proxyUserHelpText')}
helpText={t("help.proxyUserHelpText")}
/>
</div>
</div>
@@ -100,9 +100,9 @@ class ProxySettings extends React.Component<Props> {
<AddEntryToTableField
addEntry={this.addProxyExclude}
disabled={!enableProxy || !hasUpdatePermission}
buttonLabel={t('proxy-settings.add-proxy-exclude-button')}
fieldLabel={t('proxy-settings.add-proxy-exclude-textfield')}
errorMessage={t('proxy-settings.add-proxy-exclude-error')}
buttonLabel={t("proxy-settings.add-proxy-exclude-button")}
fieldLabel={t("proxy-settings.add-proxy-exclude-textfield")}
errorMessage={t("proxy-settings.add-proxy-exclude-error")}
/>
</div>
</div>
@@ -111,19 +111,19 @@ class ProxySettings extends React.Component<Props> {
}
handleProxyPasswordChange = (value: string) => {
this.props.onChange(true, value, 'proxyPassword');
this.props.onChange(true, value, "proxyPassword");
};
handleProxyPortChange = (value: string) => {
this.props.onChange(true, value, 'proxyPort');
this.props.onChange(true, value, "proxyPort");
};
handleProxyServerChange = (value: string) => {
this.props.onChange(true, value, 'proxyServer');
this.props.onChange(true, value, "proxyServer");
};
handleProxyUserChange = (value: string) => {
this.props.onChange(true, value, 'proxyUser');
this.props.onChange(true, value, "proxyUser");
};
handleEnableProxyChange = (value: string) => {
this.props.onChange(true, value, 'enableProxy');
this.props.onChange(true, value, "enableProxy");
};
addProxyExclude = (proxyExcludeName: string) => {
@@ -133,7 +133,7 @@ class ProxySettings extends React.Component<Props> {
this.props.onChange(
true,
[...this.props.proxyExcludes, proxyExcludeName],
'proxyExcludes',
"proxyExcludes"
);
};
@@ -142,4 +142,4 @@ class ProxySettings extends React.Component<Props> {
};
}
export default translate('config')(ProxySettings);
export default translate("config")(ProxySettings);

View File

@@ -1,8 +1,8 @@
import React from 'react';
import React from "react";
import {
RemoveEntryOfTableButton,
LabelWithHelpIcon,
} from '@scm-manager/ui-components';
LabelWithHelpIcon
} from "@scm-manager/ui-components";
type Props = {
items: string[];

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { translate } from 'react-i18next';
import ArrayConfigTable from './ArrayConfigTable';
import React from "react";
import { translate } from "react-i18next";
import ArrayConfigTable from "./ArrayConfigTable";
type Props = {
proxyExcludes: string[];
@@ -17,18 +17,18 @@ class ProxyExcludesTable extends React.Component<Props, State> {
return (
<ArrayConfigTable
items={proxyExcludes}
label={t('proxy-settings.proxy-excludes')}
removeLabel={t('proxy-settings.remove-proxy-exclude-button')}
label={t("proxy-settings.proxy-excludes")}
removeLabel={t("proxy-settings.remove-proxy-exclude-button")}
onRemove={this.removeEntry}
disabled={disabled}
helpText={t('help.proxyExcludesHelpText')}
helpText={t("help.proxyExcludesHelpText")}
/>
);
}
removeEntry = (newExcludes: string[]) => {
this.props.onChange(true, newExcludes, 'proxyExcludes');
this.props.onChange(true, newExcludes, "proxyExcludes");
};
}
export default translate('config')(ProxyExcludesTable);
export default translate("config")(ProxyExcludesTable);

View File

@@ -1,29 +1,29 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Redirect, Route, Switch } from 'react-router-dom';
import { ExtensionPoint } from '@scm-manager/ui-extensions';
import { History } from 'history';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { Links } from '@scm-manager/ui-types';
import React from "react";
import { translate } from "react-i18next";
import { Redirect, Route, Switch } from "react-router-dom";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { History } from "history";
import { connect } from "react-redux";
import { compose } from "redux";
import { Links } from "@scm-manager/ui-types";
import {
Page,
Navigation,
NavLink,
Section,
SubNavigation,
} from '@scm-manager/ui-components';
SubNavigation
} from "@scm-manager/ui-components";
import {
getLinks,
getAvailablePluginsLink,
getInstalledPluginsLink,
} from '../../modules/indexResource';
import AdminDetails from './AdminDetails';
import PluginsOverview from '../plugins/containers/PluginsOverview';
import GlobalConfig from './GlobalConfig';
import RepositoryRoles from '../roles/containers/RepositoryRoles';
import SingleRepositoryRole from '../roles/containers/SingleRepositoryRole';
import CreateRepositoryRole from '../roles/containers/CreateRepositoryRole';
getInstalledPluginsLink
} from "../../modules/indexResource";
import AdminDetails from "./AdminDetails";
import PluginsOverview from "../plugins/containers/PluginsOverview";
import GlobalConfig from "./GlobalConfig";
import RepositoryRoles from "../roles/containers/RepositoryRoles";
import SingleRepositoryRole from "../roles/containers/SingleRepositoryRole";
import CreateRepositoryRole from "../roles/containers/CreateRepositoryRole";
type Props = {
links: Links;
@@ -38,8 +38,8 @@ type Props = {
class Admin extends React.Component<Props> {
stripEndingSlash = (url: string) => {
if (url.endsWith('/')) {
if (url.includes('role')) {
if (url.endsWith("/")) {
if (url.includes("role")) {
return url.substring(0, url.length - 2);
}
return url.substring(0, url.length - 1);
@@ -63,7 +63,7 @@ class Admin extends React.Component<Props> {
const url = this.matchedUrl();
const extensionProps = {
links,
url,
url
};
return (
@@ -157,28 +157,28 @@ class Admin extends React.Component<Props> {
</div>
<div className="column is-one-quarter">
<Navigation>
<Section label={t('admin.menu.navigationLabel')}>
<Section label={t("admin.menu.navigationLabel")}>
<NavLink
to={`${url}/info`}
icon="fas fa-info-circle"
label={t('admin.menu.informationNavLink')}
label={t("admin.menu.informationNavLink")}
/>
{(availablePluginsLink || installedPluginsLink) && (
<SubNavigation
to={`${url}/plugins/`}
icon="fas fa-puzzle-piece"
label={t('plugins.menu.pluginsNavLink')}
label={t("plugins.menu.pluginsNavLink")}
>
{installedPluginsLink && (
<NavLink
to={`${url}/plugins/installed/`}
label={t('plugins.menu.installedNavLink')}
label={t("plugins.menu.installedNavLink")}
/>
)}
{availablePluginsLink && (
<NavLink
to={`${url}/plugins/available/`}
label={t('plugins.menu.availableNavLink')}
label={t("plugins.menu.availableNavLink")}
/>
)}
</SubNavigation>
@@ -186,7 +186,7 @@ class Admin extends React.Component<Props> {
<NavLink
to={`${url}/roles/`}
icon="fas fa-user-shield"
label={t('repositoryRole.navLink')}
label={t("repositoryRole.navLink")}
activeWhenMatch={this.matchesRoles}
activeOnlyWhenExact={false}
/>
@@ -197,11 +197,11 @@ class Admin extends React.Component<Props> {
/>
<SubNavigation
to={`${url}/settings/general`}
label={t('admin.menu.settingsNavLink')}
label={t("admin.menu.settingsNavLink")}
>
<NavLink
to={`${url}/settings/general`}
label={t('admin.menu.generalNavLink')}
label={t("admin.menu.generalNavLink")}
/>
<ExtensionPoint
name="admin.setting"
@@ -225,11 +225,11 @@ const mapStateToProps = (state: any) => {
return {
links,
availablePluginsLink,
installedPluginsLink,
installedPluginsLink
};
};
export default compose(
connect(mapStateToProps),
translate('admin'),
translate("admin")
)(Admin);

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import styled from 'styled-components';
import { Image, Loading, Subtitle, Title } from '@scm-manager/ui-components';
import { getAppVersion } from '../../modules/indexResource';
import React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import styled from "styled-components";
import { Image, Loading, Subtitle, Title } from "@scm-manager/ui-components";
import { getAppVersion } from "../../modules/indexResource";
type Props = {
loading: boolean;
@@ -33,28 +33,28 @@ class AdminDetails extends React.Component<Props> {
return (
<>
<Title title={t('admin.info.currentAppVersion')} />
<Title title={t("admin.info.currentAppVersion")} />
<Subtitle subtitle={this.props.version} />
<BoxShadowBox className="box">
<article className="media">
<ImageWrapper className="media-left">
<Image
src="/images/iconCommunitySupport.png"
alt={t('admin.info.communityIconAlt')}
alt={t("admin.info.communityIconAlt")}
/>
</ImageWrapper>
<div className="media-content">
<div className="content">
<h3 className="has-text-weight-medium">
{t('admin.info.communityTitle')}
{t("admin.info.communityTitle")}
</h3>
<p>{t('admin.info.communityInfo')}</p>
<p>{t("admin.info.communityInfo")}</p>
<a
className="button is-info is-pulled-right"
target="_blank"
href="https://scm-manager.org/support/"
>
{t('admin.info.communityButton')}
{t("admin.info.communityButton")}
</a>
</div>
</div>
@@ -65,25 +65,25 @@ class AdminDetails extends React.Component<Props> {
<ImageWrapper className="media-left">
<Image
src="/images/iconEnterpriseSupport.png"
alt={t('admin.info.enterpriseIconAlt')}
alt={t("admin.info.enterpriseIconAlt")}
/>
</ImageWrapper>
<div className="media-content">
<div className="content">
<h3 className="has-text-weight-medium">
{t('admin.info.enterpriseTitle')}
{t("admin.info.enterpriseTitle")}
</h3>
<p>
{t('admin.info.enterpriseInfo')}
{t("admin.info.enterpriseInfo")}
<br />
<strong>{t('admin.info.enterprisePartner')}</strong>
<strong>{t("admin.info.enterprisePartner")}</strong>
</p>
<a
className="button is-info is-pulled-right is-normal"
target="_blank"
href={t('admin.info.enterpriseLink')}
href={t("admin.info.enterpriseLink")}
>
{t('admin.info.enterpriseButton')}
{t("admin.info.enterpriseButton")}
</a>
</div>
</div>
@@ -97,8 +97,8 @@ class AdminDetails extends React.Component<Props> {
const mapStateToProps = (state: any) => {
const version = getAppVersion(state);
return {
version,
version
};
};
export default connect(mapStateToProps)(translate('admin')(AdminDetails));
export default connect(mapStateToProps)(translate("admin")(AdminDetails));

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Title, Loading, ErrorNotification } from '@scm-manager/ui-components';
import React from "react";
import { translate } from "react-i18next";
import { Title, Loading, ErrorNotification } from "@scm-manager/ui-components";
import {
fetchConfig,
getFetchConfigFailure,
@@ -10,18 +10,18 @@ import {
isModifyConfigPending,
getConfigUpdatePermission,
getModifyConfigFailure,
modifyConfigReset,
} from '../modules/config';
import { connect } from 'react-redux';
import { Config, NamespaceStrategies } from '@scm-manager/ui-types';
import ConfigForm from '../components/form/ConfigForm';
import { getConfigLink } from '../../modules/indexResource';
modifyConfigReset
} from "../modules/config";
import { connect } from "react-redux";
import { Config, NamespaceStrategies } from "@scm-manager/ui-types";
import ConfigForm from "../components/form/ConfigForm";
import { getConfigLink } from "../../modules/indexResource";
import {
fetchNamespaceStrategiesIfNeeded,
getFetchNamespaceStrategiesFailure,
getNamespaceStrategies,
isFetchNamespaceStrategiesPending,
} from '../modules/namespaceStrategies';
isFetchNamespaceStrategiesPending
} from "../modules/namespaceStrategies";
type Props = {
loading: boolean;
@@ -52,7 +52,7 @@ class GlobalConfig extends React.Component<Props, State> {
this.state = {
configReadPermission: true,
configChanged: false,
configChanged: false
};
}
@@ -63,7 +63,7 @@ class GlobalConfig extends React.Component<Props, State> {
this.props.fetchConfig(this.props.configLink);
} else {
this.setState({
configReadPermission: false,
configReadPermission: false
});
}
}
@@ -71,7 +71,7 @@ class GlobalConfig extends React.Component<Props, State> {
modifyConfig = (config: Config) => {
this.props.modifyConfig(config);
this.setState({
configChanged: true,
configChanged: true
});
};
@@ -83,11 +83,11 @@ class GlobalConfig extends React.Component<Props, State> {
className="delete"
onClick={() =>
this.setState({
configChanged: false,
configChanged: false
})
}
/>
{this.props.t('config.form.submit-success-notification')}
{this.props.t("config.form.submit-success-notification")}
</div>
);
}
@@ -103,7 +103,7 @@ class GlobalConfig extends React.Component<Props, State> {
return (
<div>
<Title title={t('config.title')} />
<Title title={t("config.title")} />
{this.renderError()}
{this.renderContent()}
</div>
@@ -124,7 +124,7 @@ class GlobalConfig extends React.Component<Props, State> {
loading,
config,
configUpdatePermission,
namespaceStrategies,
namespaceStrategies
} = this.props;
const { configReadPermission } = this.state;
if (!error) {
@@ -159,7 +159,7 @@ const mapDispatchToProps = dispatch => {
},
fetchNamespaceStrategiesIfNeeded: () => {
dispatch(fetchNamespaceStrategiesIfNeeded());
},
}
};
};
@@ -184,11 +184,11 @@ const mapStateToProps = state => {
config,
configUpdatePermission,
configLink,
namespaceStrategies,
namespaceStrategies
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(translate('config')(GlobalConfig));
mapDispatchToProps
)(translate("config")(GlobalConfig));

View File

@@ -1,6 +1,6 @@
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_CONFIG,
@@ -19,118 +19,118 @@ import reducer, {
isModifyConfigPending,
getModifyConfigFailure,
getConfig,
getConfigUpdatePermission,
} from './config';
getConfigUpdatePermission
} from "./config";
const CONFIG_URL = '/config';
const URL = '/api/v2' + CONFIG_URL;
const CONFIG_URL = "/config";
const URL = "/api/v2" + CONFIG_URL;
const error = new Error('You have an error!');
const error = new Error("You have an error!");
const config = {
proxyPassword: null,
proxyPort: 8080,
proxyServer: 'proxy.mydomain.com',
proxyServer: "proxy.mydomain.com",
proxyUser: null,
enableProxy: false,
realmDescription: 'SONIA :: SCM Manager',
realmDescription: "SONIA :: SCM Manager",
disableGroupingGrid: false,
dateFormat: 'YYYY-MM-DD HH:mm:ss',
dateFormat: "YYYY-MM-DD HH:mm:ss",
anonymousAccessEnabled: false,
adminGroups: [],
adminUsers: [],
baseUrl: 'http://localhost:8081',
baseUrl: "http://localhost:8081",
forceBaseUrl: false,
loginAttemptLimit: -1,
proxyExcludes: [],
skipFailedAuthenticators: false,
pluginUrl:
'http://plugins.scm-manager.org/scm-plugin-backend/api/{version}/plugins?os={os}&arch={arch}&snapshot=false',
"http://plugins.scm-manager.org/scm-plugin-backend/api/{version}/plugins?os={os}&arch={arch}&snapshot=false",
loginAttemptLimitTimeout: 300,
enabledXsrfProtection: true,
namespaceStrategy: 'UsernameNamespaceStrategy',
namespaceStrategy: "UsernameNamespaceStrategy",
_links: {
self: {
href: 'http://localhost:8081/api/v2/config',
href: "http://localhost:8081/api/v2/config"
},
update: {
href: 'http://localhost:8081/api/v2/config',
},
},
href: "http://localhost:8081/api/v2/config"
}
}
};
const configWithNullValues = {
proxyPassword: null,
proxyPort: 8080,
proxyServer: 'proxy.mydomain.com',
proxyServer: "proxy.mydomain.com",
proxyUser: null,
enableProxy: false,
realmDescription: 'SONIA :: SCM Manager',
realmDescription: "SONIA :: SCM Manager",
disableGroupingGrid: false,
dateFormat: 'YYYY-MM-DD HH:mm:ss',
dateFormat: "YYYY-MM-DD HH:mm:ss",
anonymousAccessEnabled: false,
adminGroups: null,
adminUsers: null,
baseUrl: 'http://localhost:8081',
baseUrl: "http://localhost:8081",
forceBaseUrl: false,
loginAttemptLimit: -1,
proxyExcludes: null,
skipFailedAuthenticators: false,
pluginUrl:
'http://plugins.scm-manager.org/scm-plugin-backend/api/{version}/plugins?os={os}&arch={arch}&snapshot=false',
"http://plugins.scm-manager.org/scm-plugin-backend/api/{version}/plugins?os={os}&arch={arch}&snapshot=false",
loginAttemptLimitTimeout: 300,
enabledXsrfProtection: true,
namespaceStrategy: 'UsernameNamespaceStrategy',
namespaceStrategy: "UsernameNamespaceStrategy",
_links: {
self: {
href: 'http://localhost:8081/api/v2/config',
href: "http://localhost:8081/api/v2/config"
},
update: {
href: 'http://localhost:8081/api/v2/config',
},
},
href: "http://localhost:8081/api/v2/config"
}
}
};
const responseBody = {
entries: config,
configUpdatePermission: false,
configUpdatePermission: false
};
const response = {
headers: {
'content-type': 'application/json',
"content-type": "application/json"
},
responseBody,
responseBody
};
describe('config fetch()', () => {
describe("config fetch()", () => {
const mockStore = configureMockStore([thunk]);
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it('should successfully fetch config', () => {
it("should successfully fetch config", () => {
fetchMock.getOnce(URL, response);
const expectedActions = [
{
type: FETCH_CONFIG_PENDING,
type: FETCH_CONFIG_PENDING
},
{
type: FETCH_CONFIG_SUCCESS,
payload: response,
},
payload: response
}
];
const store = mockStore({
indexResources: {
links: {
config: {
href: CONFIG_URL,
},
},
},
href: CONFIG_URL
}
}
}
});
return store.dispatch(fetchConfig(CONFIG_URL)).then(() => {
@@ -138,19 +138,19 @@ describe('config fetch()', () => {
});
});
it('should fail getting config on HTTP 500', () => {
it("should fail getting config on HTTP 500", () => {
fetchMock.getOnce(URL, {
status: 500,
status: 500
});
const store = mockStore({
indexResources: {
links: {
config: {
href: CONFIG_URL,
},
},
},
href: CONFIG_URL
}
}
}
});
return store.dispatch(fetchConfig(CONFIG_URL)).then(() => {
const actions = store.getActions();
@@ -160,9 +160,9 @@ describe('config fetch()', () => {
});
});
it('should successfully modify config', () => {
fetchMock.putOnce('http://localhost:8081/api/v2/config', {
status: 204,
it("should successfully modify config", () => {
fetchMock.putOnce("http://localhost:8081/api/v2/config", {
status: 204
});
const store = mockStore({});
@@ -175,9 +175,9 @@ describe('config fetch()', () => {
});
});
it('should call the callback after modifying config', () => {
fetchMock.putOnce('http://localhost:8081/api/v2/config', {
status: 204,
it("should call the callback after modifying config", () => {
fetchMock.putOnce("http://localhost:8081/api/v2/config", {
status: 204
});
let called = false;
@@ -194,9 +194,9 @@ describe('config fetch()', () => {
});
});
it('should fail modifying config on HTTP 500', () => {
fetchMock.putOnce('http://localhost:8081/api/v2/config', {
status: 500,
it("should fail modifying config on HTTP 500", () => {
fetchMock.putOnce("http://localhost:8081/api/v2/config", {
status: 500
});
const store = mockStore({});
@@ -210,28 +210,28 @@ describe('config fetch()', () => {
});
});
describe('config reducer', () => {
it('should update state correctly according to FETCH_CONFIG_SUCCESS action', () => {
describe("config reducer", () => {
it("should update state correctly according to FETCH_CONFIG_SUCCESS action", () => {
const newState = reducer({}, fetchConfigSuccess(config));
expect(newState).toEqual({
entries: config,
configUpdatePermission: true,
configUpdatePermission: true
});
});
it('should set configUpdatePermission to true if update link is present', () => {
it("should set configUpdatePermission to true if update link is present", () => {
const newState = reducer({}, fetchConfigSuccess(config));
expect(newState.configUpdatePermission).toBeTruthy();
});
it('should update state according to FETCH_CONFIG_SUCCESS action', () => {
it("should update state according to FETCH_CONFIG_SUCCESS action", () => {
const newState = reducer({}, fetchConfigSuccess(config));
expect(newState.entries).toBe(config);
});
it('should return empty arrays for null values', () => {
it("should return empty arrays for null values", () => {
// $FlowFixMe
const config = reducer({}, fetchConfigSuccess(configWithNullValues))
.entries;
@@ -241,73 +241,73 @@ describe('config reducer', () => {
});
});
describe('selector tests', () => {
it('should return true, when fetch config is pending', () => {
describe("selector tests", () => {
it("should return true, when fetch config is pending", () => {
const state = {
pending: {
[FETCH_CONFIG]: true,
},
[FETCH_CONFIG]: true
}
};
expect(isFetchConfigPending(state)).toEqual(true);
});
it('should return false, when fetch config is not pending', () => {
it("should return false, when fetch config is not pending", () => {
expect(isFetchConfigPending({})).toEqual(false);
});
it('should return error when fetch config did fail', () => {
it("should return error when fetch config did fail", () => {
const state = {
failure: {
[FETCH_CONFIG]: error,
},
[FETCH_CONFIG]: error
}
};
expect(getFetchConfigFailure(state)).toEqual(error);
});
it('should return undefined when fetch config did not fail', () => {
it("should return undefined when fetch config did not fail", () => {
expect(getFetchConfigFailure({})).toBe(undefined);
});
it('should return true, when modify group is pending', () => {
it("should return true, when modify group is pending", () => {
const state = {
pending: {
[MODIFY_CONFIG]: true,
},
[MODIFY_CONFIG]: true
}
};
expect(isModifyConfigPending(state)).toEqual(true);
});
it('should return false, when modify config is not pending', () => {
it("should return false, when modify config is not pending", () => {
expect(isModifyConfigPending({})).toEqual(false);
});
it('should return error when modify config did fail', () => {
it("should return error when modify config did fail", () => {
const state = {
failure: {
[MODIFY_CONFIG]: error,
},
[MODIFY_CONFIG]: error
}
};
expect(getModifyConfigFailure(state)).toEqual(error);
});
it('should return undefined when modify config did not fail', () => {
it("should return undefined when modify config did not fail", () => {
expect(getModifyConfigFailure({})).toBe(undefined);
});
it('should return config', () => {
it("should return config", () => {
const state = {
config: {
entries: config,
},
entries: config
}
};
expect(getConfig(state)).toEqual(config);
});
it('should return configUpdatePermission', () => {
it("should return configUpdatePermission", () => {
const state = {
config: {
configUpdatePermission: true,
},
configUpdatePermission: true
}
};
expect(getConfigUpdatePermission(state)).toEqual(true);
});

View File

@@ -1,23 +1,23 @@
import { apiClient } from '@scm-manager/ui-components';
import * as types from '../../modules/types';
import { Action } from '@scm-manager/ui-types';
import { isPending } from '../../modules/pending';
import { getFailure } from '../../modules/failure';
import { Dispatch } from 'redux';
import { Config } from '@scm-manager/ui-types';
import { apiClient } from "@scm-manager/ui-components";
import * as types from "../../modules/types";
import { Action } from "@scm-manager/ui-types";
import { isPending } from "../../modules/pending";
import { getFailure } from "../../modules/failure";
import { Dispatch } from "redux";
import { Config } from "@scm-manager/ui-types";
export const FETCH_CONFIG = 'scm/config/FETCH_CONFIG';
export const FETCH_CONFIG = "scm/config/FETCH_CONFIG";
export const FETCH_CONFIG_PENDING = `${FETCH_CONFIG}_${types.PENDING_SUFFIX}`;
export const FETCH_CONFIG_SUCCESS = `${FETCH_CONFIG}_${types.SUCCESS_SUFFIX}`;
export const FETCH_CONFIG_FAILURE = `${FETCH_CONFIG}_${types.FAILURE_SUFFIX}`;
export const MODIFY_CONFIG = 'scm/config/MODIFY_CONFIG';
export const MODIFY_CONFIG = "scm/config/MODIFY_CONFIG";
export const MODIFY_CONFIG_PENDING = `${MODIFY_CONFIG}_${types.PENDING_SUFFIX}`;
export const MODIFY_CONFIG_SUCCESS = `${MODIFY_CONFIG}_${types.SUCCESS_SUFFIX}`;
export const MODIFY_CONFIG_FAILURE = `${MODIFY_CONFIG}_${types.FAILURE_SUFFIX}`;
export const MODIFY_CONFIG_RESET = `${MODIFY_CONFIG}_${types.RESET_SUFFIX}`;
const CONTENT_TYPE_CONFIG = 'application/vnd.scmm-config+json;v=2';
const CONTENT_TYPE_CONFIG = "application/vnd.scmm-config+json;v=2";
//fetch config
export function fetchConfig(link: string) {
@@ -39,14 +39,14 @@ export function fetchConfig(link: string) {
export function fetchConfigPending(): Action {
return {
type: FETCH_CONFIG_PENDING,
type: FETCH_CONFIG_PENDING
};
}
export function fetchConfigSuccess(config: Config): Action {
return {
type: FETCH_CONFIG_SUCCESS,
payload: config,
payload: config
};
}
@@ -54,8 +54,8 @@ export function fetchConfigFailure(error: Error): Action {
return {
type: FETCH_CONFIG_FAILURE,
payload: {
error,
},
error
}
};
}
@@ -80,14 +80,14 @@ export function modifyConfig(config: Config, callback?: () => void) {
export function modifyConfigPending(config: Config): Action {
return {
type: MODIFY_CONFIG_PENDING,
payload: config,
payload: config
};
}
export function modifyConfigSuccess(config: Config): Action {
return {
type: MODIFY_CONFIG_SUCCESS,
payload: config,
payload: config
};
}
@@ -96,14 +96,14 @@ export function modifyConfigFailure(config: Config, error: Error): Action {
type: MODIFY_CONFIG_FAILURE,
payload: {
error,
config,
},
config
}
};
}
export function modifyConfigReset() {
return {
type: MODIFY_CONFIG_RESET,
type: MODIFY_CONFIG_RESET
};
}
@@ -130,7 +130,7 @@ function reducer(state: any = {}, action: any = {}) {
return {
...state,
entries: config,
configUpdatePermission: action.payload._links.update ? true : false,
configUpdatePermission: action.payload._links.update ? true : false
};
default:
return state;

View File

@@ -1,6 +1,6 @@
import fetchMock from 'fetch-mock';
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 {
FETCH_NAMESPACESTRATEGIES_TYPES,
FETCH_NAMESPACESTRATEGIES_TYPES_FAILURE,
@@ -12,67 +12,67 @@ import {
default as reducer,
getNamespaceStrategies,
isFetchNamespaceStrategiesPending,
getFetchNamespaceStrategiesFailure,
} from './namespaceStrategies';
import { MODIFY_CONFIG_SUCCESS } from './config';
getFetchNamespaceStrategiesFailure
} from "./namespaceStrategies";
import { MODIFY_CONFIG_SUCCESS } from "./config";
const strategies = {
current: 'UsernameNamespaceStrategy',
current: "UsernameNamespaceStrategy",
available: [
'UsernameNamespaceStrategy',
'CustomNamespaceStrategy',
'CurrentYearNamespaceStrategy',
'RepositoryTypeNamespaceStrategy',
"UsernameNamespaceStrategy",
"CustomNamespaceStrategy",
"CurrentYearNamespaceStrategy",
"RepositoryTypeNamespaceStrategy"
],
_links: {
self: {
href: 'http://localhost:8081/scm/api/v2/namespaceStrategies',
},
},
href: "http://localhost:8081/scm/api/v2/namespaceStrategies"
}
}
};
describe('namespace strategy caching', () => {
it('should fetch strategies, on empty state', () => {
describe("namespace strategy caching", () => {
it("should fetch strategies, on empty state", () => {
expect(shouldFetchNamespaceStrategies({})).toBe(true);
});
it('should fetch strategies, on empty namespaceStrategies node', () => {
it("should fetch strategies, on empty namespaceStrategies node", () => {
const state = {
namespaceStrategies: {},
namespaceStrategies: {}
};
expect(shouldFetchNamespaceStrategies(state)).toBe(true);
});
it('should not fetch strategies, on pending state', () => {
it("should not fetch strategies, on pending state", () => {
const state = {
pending: {
[FETCH_NAMESPACESTRATEGIES_TYPES]: true,
},
[FETCH_NAMESPACESTRATEGIES_TYPES]: true
}
};
expect(shouldFetchNamespaceStrategies(state)).toBe(false);
});
it('should not fetch strategies, on failure state', () => {
it("should not fetch strategies, on failure state", () => {
const state = {
failure: {
[FETCH_NAMESPACESTRATEGIES_TYPES]: new Error('no...'),
},
[FETCH_NAMESPACESTRATEGIES_TYPES]: new Error("no...")
}
};
expect(shouldFetchNamespaceStrategies(state)).toBe(false);
});
it('should not fetch strategies, if they are already fetched', () => {
it("should not fetch strategies, if they are already fetched", () => {
const state = {
namespaceStrategies: {
current: 'some',
},
current: "some"
}
};
expect(shouldFetchNamespaceStrategies(state)).toBe(false);
});
});
describe('namespace strategies fetch', () => {
const URL = 'http://scm.hitchhiker.com/api/v2/namespaceStrategies';
describe("namespace strategies fetch", () => {
const URL = "http://scm.hitchhiker.com/api/v2/namespaceStrategies";
const mockStore = configureMockStore([thunk]);
afterEach(() => {
@@ -86,24 +86,24 @@ describe('namespace strategies fetch', () => {
indexResources: {
links: {
namespaceStrategies: {
href: URL,
},
},
},
href: URL
}
}
}
});
};
it('should successfully fetch strategies', () => {
it("should successfully fetch strategies", () => {
fetchMock.getOnce(URL, strategies);
const expectedActions = [
{
type: FETCH_NAMESPACESTRATEGIES_TYPES_PENDING,
type: FETCH_NAMESPACESTRATEGIES_TYPES_PENDING
},
{
type: FETCH_NAMESPACESTRATEGIES_TYPES_SUCCESS,
payload: strategies,
},
payload: strategies
}
];
const store = createStore();
@@ -112,9 +112,9 @@ describe('namespace strategies fetch', () => {
});
});
it('should dispatch FETCH_NAMESPACESTRATEGIES_TYPES_FAILURE on server error', () => {
it("should dispatch FETCH_NAMESPACESTRATEGIES_TYPES_FAILURE on server error", () => {
fetchMock.getOnce(URL, {
status: 500,
status: 500
});
const store = createStore();
@@ -126,75 +126,75 @@ describe('namespace strategies fetch', () => {
});
});
it('should not dispatch any action, if the strategies are already fetched', () => {
it("should not dispatch any action, if the strategies are already fetched", () => {
const store = createStore({
namespaceStrategies: strategies,
namespaceStrategies: strategies
});
store.dispatch(fetchNamespaceStrategiesIfNeeded());
expect(store.getActions().length).toBe(0);
});
});
describe('namespace strategies reducer', () => {
it('should return unmodified state on unknown action', () => {
describe("namespace strategies reducer", () => {
it("should return unmodified state on unknown action", () => {
const state = {};
expect(reducer(state)).toBe(state);
});
it('should store the strategies on success', () => {
it("should store the strategies on success", () => {
const newState = reducer({}, fetchNamespaceStrategiesSuccess(strategies));
expect(newState).toBe(strategies);
});
it('should clear store if config was modified', () => {
it("should clear store if config was modified", () => {
const modifyConfigAction = {
type: MODIFY_CONFIG_SUCCESS,
payload: {
namespaceStrategy: 'CustomNamespaceStrategy',
},
namespaceStrategy: "CustomNamespaceStrategy"
}
};
const newState = reducer(strategies, modifyConfigAction);
expect(newState.current).toEqual('CustomNamespaceStrategy');
expect(newState.current).toEqual("CustomNamespaceStrategy");
});
});
describe('namespace strategy selectors', () => {
const error = new Error('The end of the universe');
describe("namespace strategy selectors", () => {
const error = new Error("The end of the universe");
it('should return an empty object', () => {
it("should return an empty object", () => {
expect(getNamespaceStrategies({})).toEqual({});
});
it('should return the namespace strategies', () => {
it("should return the namespace strategies", () => {
const state = {
namespaceStrategies: strategies,
namespaceStrategies: strategies
};
expect(getNamespaceStrategies(state)).toBe(strategies);
});
it('should return true, when fetch namespace strategies is pending', () => {
it("should return true, when fetch namespace strategies is pending", () => {
const state = {
pending: {
[FETCH_NAMESPACESTRATEGIES_TYPES]: true,
},
[FETCH_NAMESPACESTRATEGIES_TYPES]: true
}
};
expect(isFetchNamespaceStrategiesPending(state)).toEqual(true);
});
it('should return false, when fetch strategies is not pending', () => {
it("should return false, when fetch strategies is not pending", () => {
expect(isFetchNamespaceStrategiesPending({})).toEqual(false);
});
it('should return error when fetch namespace strategies did fail', () => {
it("should return error when fetch namespace strategies did fail", () => {
const state = {
failure: {
[FETCH_NAMESPACESTRATEGIES_TYPES]: error,
},
[FETCH_NAMESPACESTRATEGIES_TYPES]: error
}
};
expect(getFetchNamespaceStrategiesFailure(state)).toEqual(error);
});
it('should return undefined when fetch strategies did not fail', () => {
it("should return undefined when fetch strategies did not fail", () => {
expect(getFetchNamespaceStrategiesFailure({})).toBe(undefined);
});
});

View File

@@ -1,12 +1,12 @@
import * as types from '../../modules/types';
import { Action, NamespaceStrategies } from '@scm-manager/ui-types';
import { apiClient } from '@scm-manager/ui-components';
import { isPending } from '../../modules/pending';
import { getFailure } from '../../modules/failure';
import { MODIFY_CONFIG_SUCCESS } from './config';
import * as types from "../../modules/types";
import { Action, NamespaceStrategies } from "@scm-manager/ui-types";
import { apiClient } from "@scm-manager/ui-components";
import { isPending } from "../../modules/pending";
import { getFailure } from "../../modules/failure";
import { MODIFY_CONFIG_SUCCESS } from "./config";
export const FETCH_NAMESPACESTRATEGIES_TYPES =
'scm/config/FETCH_NAMESPACESTRATEGIES_TYPES';
"scm/config/FETCH_NAMESPACESTRATEGIES_TYPES";
export const FETCH_NAMESPACESTRATEGIES_TYPES_PENDING = `${FETCH_NAMESPACESTRATEGIES_TYPES}_${types.PENDING_SUFFIX}`;
export const FETCH_NAMESPACESTRATEGIES_TYPES_SUCCESS = `${FETCH_NAMESPACESTRATEGIES_TYPES}_${types.SUCCESS_SUFFIX}`;
export const FETCH_NAMESPACESTRATEGIES_TYPES_FAILURE = `${FETCH_NAMESPACESTRATEGIES_TYPES}_${types.FAILURE_SUFFIX}`;
@@ -17,7 +17,7 @@ export function fetchNamespaceStrategiesIfNeeded() {
if (shouldFetchNamespaceStrategies(state)) {
return fetchNamespaceStrategies(
dispatch,
state.indexResources.links.namespaceStrategies.href,
state.indexResources.links.namespaceStrategies.href
);
}
};
@@ -48,23 +48,23 @@ export function shouldFetchNamespaceStrategies(state: object) {
export function fetchNamespaceStrategiesPending(): Action {
return {
type: FETCH_NAMESPACESTRATEGIES_TYPES_PENDING,
type: FETCH_NAMESPACESTRATEGIES_TYPES_PENDING
};
}
export function fetchNamespaceStrategiesSuccess(
namespaceStrategies: NamespaceStrategies,
namespaceStrategies: NamespaceStrategies
): Action {
return {
type: FETCH_NAMESPACESTRATEGIES_TYPES_SUCCESS,
payload: namespaceStrategies,
payload: namespaceStrategies
};
}
export function fetchNamespaceStrategiesFailure(error: Error): Action {
return {
type: FETCH_NAMESPACESTRATEGIES_TYPES_FAILURE,
payload: error,
payload: error
};
}
@@ -73,8 +73,8 @@ export function fetchNamespaceStrategiesFailure(error: Error): Action {
export default function reducer(
state: object = {},
action: Action = {
type: 'UNKNOWN',
},
type: "UNKNOWN"
}
): object {
if (
action.type === FETCH_NAMESPACESTRATEGIES_TYPES_SUCCESS &&
@@ -85,7 +85,7 @@ export default function reducer(
const config = action.payload;
return {
...state,
current: config.namespaceStrategy,
current: config.namespaceStrategy
};
}
return state;

View File

@@ -1,8 +1,8 @@
import React from 'react';
import PluginActionModal from './PluginActionModal';
import { PendingPlugins } from '@scm-manager/ui-types';
import { apiClient } from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
import React from "react";
import PluginActionModal from "./PluginActionModal";
import { PendingPlugins } from "@scm-manager/ui-types";
import { apiClient } from "@scm-manager/ui-components";
import { translate } from "react-i18next";
type Props = {
onClose: () => void;
@@ -19,8 +19,8 @@ class CancelPendingActionModal extends React.Component<Props> {
return (
<PluginActionModal
description={t('plugins.modal.cancelPending')}
label={t('plugins.cancelPending')}
description={t("plugins.modal.cancelPending")}
label={t("plugins.cancelPending")}
onClose={onClose}
pendingPlugins={pendingPlugins}
execute={this.cancelPending}
@@ -37,4 +37,4 @@ class CancelPendingActionModal extends React.Component<Props> {
};
}
export default translate('admin')(CancelPendingActionModal);
export default translate("admin")(CancelPendingActionModal);

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { Button } from '@scm-manager/ui-components';
import { PendingPlugins } from '@scm-manager/ui-types';
import { translate } from 'react-i18next';
import ExecutePendingModal from './ExecutePendingModal';
import React from "react";
import { Button } from "@scm-manager/ui-components";
import { PendingPlugins } from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import ExecutePendingModal from "./ExecutePendingModal";
type Props = {
pendingPlugins: PendingPlugins;
@@ -19,19 +19,19 @@ class ExecutePendingAction extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
showModal: false,
showModal: false
};
}
openModal = () => {
this.setState({
showModal: true,
showModal: true
});
};
closeModal = () => {
this.setState({
showModal: false,
showModal: false
});
};
@@ -56,7 +56,7 @@ class ExecutePendingAction extends React.Component<Props, State> {
{this.renderModal()}
<Button
color="primary"
label={t('plugins.executePending')}
label={t("plugins.executePending")}
action={this.openModal}
/>
</>
@@ -64,4 +64,4 @@ class ExecutePendingAction extends React.Component<Props, State> {
}
}
export default translate('admin')(ExecutePendingAction);
export default translate("admin")(ExecutePendingAction);

View File

@@ -1,9 +1,9 @@
import React from 'react';
import PluginActionModal from './PluginActionModal';
import { PendingPlugins } from '@scm-manager/ui-types';
import waitForRestart from './waitForRestart';
import { apiClient, Notification } from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
import React from "react";
import PluginActionModal from "./PluginActionModal";
import { PendingPlugins } from "@scm-manager/ui-types";
import waitForRestart from "./waitForRestart";
import { apiClient, Notification } from "@scm-manager/ui-components";
import { translate } from "react-i18next";
type Props = {
onClose: () => void;
@@ -19,14 +19,14 @@ class ExecutePendingActionModal extends React.Component<Props> {
return (
<PluginActionModal
description={t('plugins.modal.executePending')}
label={t('plugins.modal.executeAndRestart')}
description={t("plugins.modal.executePending")}
label={t("plugins.modal.executeAndRestart")}
onClose={onClose}
pendingPlugins={pendingPlugins}
execute={this.executeAndRestart}
>
<Notification type="warning">
{t('plugins.modal.restartNotification')}
{t("plugins.modal.restartNotification")}
</Notification>
</PluginActionModal>
);
@@ -40,4 +40,4 @@ class ExecutePendingActionModal extends React.Component<Props> {
};
}
export default translate('admin')(ExecutePendingActionModal);
export default translate("admin")(ExecutePendingActionModal);

View File

@@ -1,16 +1,16 @@
import React from 'react';
import React from "react";
import {
apiClient,
Button,
ButtonGroup,
ErrorNotification,
Modal,
Notification,
} from '@scm-manager/ui-components';
import { PendingPlugins } from '@scm-manager/ui-types';
import { translate } from 'react-i18next';
import waitForRestart from './waitForRestart';
import SuccessNotification from './SuccessNotification';
Notification
} from "@scm-manager/ui-components";
import { PendingPlugins } from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import waitForRestart from "./waitForRestart";
import SuccessNotification from "./SuccessNotification";
type Props = {
onClose: () => void;
@@ -31,7 +31,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
super(props);
this.state = {
loading: false,
success: false,
success: false
};
}
@@ -45,7 +45,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
} else {
return (
<Notification type="warning">
{t('plugins.modal.restartNotification')}
{t("plugins.modal.restartNotification")}
</Notification>
);
}
@@ -54,7 +54,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
executeAndRestart = () => {
const { pendingPlugins } = this.props;
this.setState({
loading: true,
loading: true
});
apiClient
@@ -64,14 +64,14 @@ class ExecutePendingModal extends React.Component<Props, State> {
this.setState({
success: true,
loading: false,
error: undefined,
error: undefined
});
})
.catch(error => {
this.setState({
success: false,
loading: false,
error: error,
error: error
});
});
};
@@ -82,7 +82,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
<>
{pendingPlugins._embedded && pendingPlugins._embedded.new.length > 0 && (
<>
<strong>{t('plugins.modal.installQueue')}</strong>
<strong>{t("plugins.modal.installQueue")}</strong>
<ul>
{pendingPlugins._embedded.new.map(plugin => (
<li key={plugin.name}>{plugin.name}</li>
@@ -101,7 +101,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
{pendingPlugins._embedded &&
pendingPlugins._embedded.update.length > 0 && (
<>
<strong>{t('plugins.modal.updateQueue')}</strong>
<strong>{t("plugins.modal.updateQueue")}</strong>
<ul>
{pendingPlugins._embedded.update.map(plugin => (
<li key={plugin.name}>{plugin.name}</li>
@@ -120,7 +120,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
{pendingPlugins._embedded &&
pendingPlugins._embedded.uninstall.length > 0 && (
<>
<strong>{t('plugins.modal.uninstallQueue')}</strong>
<strong>{t("plugins.modal.uninstallQueue")}</strong>
<ul>
{pendingPlugins._embedded.uninstall.map(plugin => (
<li key={plugin.name}>{plugin.name}</li>
@@ -138,7 +138,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
<>
<div className="media">
<div className="content">
<p>{t('plugins.modal.executePending')}</p>
<p>{t("plugins.modal.executePending")}</p>
{this.renderInstallQueue()}
{this.renderUpdateQueue()}
{this.renderUninstallQueue()}
@@ -156,12 +156,12 @@ class ExecutePendingModal extends React.Component<Props, State> {
<ButtonGroup>
<Button
color="warning"
label={t('plugins.modal.executeAndRestart')}
label={t("plugins.modal.executeAndRestart")}
loading={loading}
action={this.executeAndRestart}
disabled={error || success}
/>
<Button label={t('plugins.modal.abort')} action={onClose} />
<Button label={t("plugins.modal.abort")} action={onClose} />
</ButtonGroup>
);
};
@@ -170,7 +170,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
const { onClose, t } = this.props;
return (
<Modal
title={t('plugins.modal.executeAndRestart')}
title={t("plugins.modal.executeAndRestart")}
closeFunction={onClose}
body={this.renderBody()}
footer={this.renderFooter()}
@@ -180,4 +180,4 @@ class ExecutePendingModal extends React.Component<Props, State> {
}
}
export default translate('admin')(ExecutePendingModal);
export default translate("admin")(ExecutePendingModal);

View File

@@ -1,13 +1,13 @@
import * as React from 'react';
import * as React from "react";
import {
Button,
ButtonGroup,
ErrorNotification,
Modal,
} from '@scm-manager/ui-components';
import { PendingPlugins, PluginCollection } from '@scm-manager/ui-types';
import { translate } from 'react-i18next';
import SuccessNotification from './SuccessNotification';
Modal
} from "@scm-manager/ui-components";
import { PendingPlugins, PluginCollection } from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import SuccessNotification from "./SuccessNotification";
type Props = {
onClose: () => void;
@@ -36,7 +36,7 @@ class PluginActionModal extends React.Component<Props, State> {
super(props);
this.state = {
loading: false,
success: false,
success: false
};
}
@@ -54,7 +54,7 @@ class PluginActionModal extends React.Component<Props, State> {
executeAction = () => {
this.setState({
loading: true,
loading: true
});
this.props
@@ -62,14 +62,14 @@ class PluginActionModal extends React.Component<Props, State> {
.then(() => {
this.setState({
success: true,
loading: false,
loading: false
});
})
.catch(error => {
this.setState({
success: false,
loading: false,
error: error,
error: error
});
});
};
@@ -93,7 +93,7 @@ class PluginActionModal extends React.Component<Props, State> {
installedPlugins._embedded &&
installedPlugins._embedded.plugins && (
<>
<strong>{t('plugins.modal.updateQueue')}</strong>
<strong>{t("plugins.modal.updateQueue")}</strong>
<ul>
{installedPlugins._embedded.plugins
.filter(plugin => plugin._links && plugin._links.update)
@@ -115,7 +115,7 @@ class PluginActionModal extends React.Component<Props, State> {
pendingPlugins._embedded &&
pendingPlugins._embedded.new.length > 0 && (
<>
<strong>{t('plugins.modal.installQueue')}</strong>
<strong>{t("plugins.modal.installQueue")}</strong>
<ul>
{pendingPlugins._embedded.new.map(plugin => (
<li key={plugin.name}>{plugin.name}</li>
@@ -135,7 +135,7 @@ class PluginActionModal extends React.Component<Props, State> {
pendingPlugins._embedded &&
pendingPlugins._embedded.update.length > 0 && (
<>
<strong>{t('plugins.modal.updateQueue')}</strong>
<strong>{t("plugins.modal.updateQueue")}</strong>
<ul>
{pendingPlugins._embedded.update.map(plugin => (
<li key={plugin.name}>{plugin.name}</li>
@@ -155,7 +155,7 @@ class PluginActionModal extends React.Component<Props, State> {
pendingPlugins._embedded &&
pendingPlugins._embedded.uninstall.length > 0 && (
<>
<strong>{t('plugins.modal.uninstallQueue')}</strong>
<strong>{t("plugins.modal.uninstallQueue")}</strong>
<ul>
{pendingPlugins._embedded.uninstall.map(plugin => (
<li key={plugin.name}>{plugin.name}</li>
@@ -193,7 +193,7 @@ class PluginActionModal extends React.Component<Props, State> {
action={this.executeAction}
disabled={error || success}
/>
<Button label={t('plugins.modal.abort')} action={onClose} />
<Button label={t("plugins.modal.abort")} action={onClose} />
</ButtonGroup>
);
};
@@ -212,4 +212,4 @@ class PluginActionModal extends React.Component<Props, State> {
}
}
export default translate('admin')(PluginActionModal);
export default translate("admin")(PluginActionModal);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { ExtensionPoint } from '@scm-manager/ui-extensions';
import { Plugin } from '@scm-manager/ui-types';
import { Image } from '@scm-manager/ui-components';
import React from "react";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import { Plugin } from "@scm-manager/ui-types";
import { Image } from "@scm-manager/ui-components";
type Props = {
plugin: Plugin;
@@ -15,11 +15,11 @@ export default class PluginAvatar extends React.Component<Props> {
<ExtensionPoint
name="plugins.plugin-avatar"
props={{
plugin,
plugin
}}
>
<Image
src={plugin.avatarUrl ? plugin.avatarUrl : '/images/blib.jpg'}
src={plugin.avatarUrl ? plugin.avatarUrl : "/images/blib.jpg"}
alt="Logo"
/>
</ExtensionPoint>

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import styled from 'styled-components';
import * as React from "react";
import styled from "styled-components";
type Props = {
children?: React.Node;

View File

@@ -1,16 +1,16 @@
import React from 'react';
import { translate } from 'react-i18next';
import classNames from 'classnames';
import styled from 'styled-components';
import { Plugin } from '@scm-manager/ui-types';
import { CardColumn, Icon } from '@scm-manager/ui-components';
import PluginAvatar from './PluginAvatar';
import PluginModal from './PluginModal';
import React from "react";
import { translate } from "react-i18next";
import classNames from "classnames";
import styled from "styled-components";
import { Plugin } from "@scm-manager/ui-types";
import { CardColumn, Icon } from "@scm-manager/ui-components";
import PluginAvatar from "./PluginAvatar";
import PluginModal from "./PluginModal";
export const PluginAction = {
INSTALL: 'install',
UPDATE: 'update',
UNINSTALL: 'uninstall',
INSTALL: "install",
UPDATE: "update",
UNINSTALL: "uninstall"
};
type Props = {
@@ -53,7 +53,7 @@ class PluginEntry extends React.Component<Props, State> {
this.state = {
showInstallModal: false,
showUpdateModal: false,
showUninstallModal: false,
showUninstallModal: false
};
}
@@ -64,7 +64,7 @@ class PluginEntry extends React.Component<Props, State> {
toggleModal = (showModal: string) => {
const oldValue = this.state[showModal];
this.setState({
[showModal]: !oldValue,
[showModal]: !oldValue
});
};
@@ -96,10 +96,10 @@ class PluginEntry extends React.Component<Props, State> {
{this.isInstallable() && (
<IconWrapper
className="level-item"
onClick={() => this.toggleModal('showInstallModal')}
onClick={() => this.toggleModal("showInstallModal")}
>
<Icon
title={t('plugins.modal.install')}
title={t("plugins.modal.install")}
name="download"
color="info"
/>
@@ -108,10 +108,10 @@ class PluginEntry extends React.Component<Props, State> {
{this.isUninstallable() && (
<IconWrapper
className="level-item"
onClick={() => this.toggleModal('showUninstallModal')}
onClick={() => this.toggleModal("showUninstallModal")}
>
<Icon
title={t('plugins.modal.uninstall')}
title={t("plugins.modal.uninstall")}
name="trash"
color="info"
/>
@@ -120,10 +120,10 @@ class PluginEntry extends React.Component<Props, State> {
{this.isUpdatable() && (
<IconWrapper
className="level-item"
onClick={() => this.toggleModal('showUpdateModal')}
onClick={() => this.toggleModal("showUpdateModal")}
>
<Icon
title={t('plugins.modal.update')}
title={t("plugins.modal.update")}
name="sync-alt"
color="info"
/>
@@ -141,7 +141,7 @@ class PluginEntry extends React.Component<Props, State> {
plugin={plugin}
pluginAction={PluginAction.INSTALL}
refresh={refresh}
onClose={() => this.toggleModal('showInstallModal')}
onClose={() => this.toggleModal("showInstallModal")}
/>
);
} else if (this.state.showUpdateModal && this.isUpdatable()) {
@@ -150,7 +150,7 @@ class PluginEntry extends React.Component<Props, State> {
plugin={plugin}
pluginAction={PluginAction.UPDATE}
refresh={refresh}
onClose={() => this.toggleModal('showUpdateModal')}
onClose={() => this.toggleModal("showUpdateModal")}
/>
);
} else if (this.state.showUninstallModal && this.isUninstallable()) {
@@ -159,7 +159,7 @@ class PluginEntry extends React.Component<Props, State> {
plugin={plugin}
pluginAction={PluginAction.UNINSTALL}
refresh={refresh}
onClose={() => this.toggleModal('showUninstallModal')}
onClose={() => this.toggleModal("showUninstallModal")}
/>
);
} else {
@@ -173,7 +173,7 @@ class PluginEntry extends React.Component<Props, State> {
<Icon
className="fa-spin fa-lg"
name="spinner"
color={plugin.markedForUninstall ? 'danger' : 'info'}
color={plugin.markedForUninstall ? "danger" : "info"}
/>
);
};
@@ -191,7 +191,7 @@ class PluginEntry extends React.Component<Props, State> {
<CardColumn
action={
this.isInstallable()
? () => this.toggleModal('showInstallModal')
? () => this.toggleModal("showInstallModal")
: null
}
avatar={avatar}
@@ -210,4 +210,4 @@ class PluginEntry extends React.Component<Props, State> {
}
}
export default translate('admin')(PluginEntry);
export default translate("admin")(PluginEntry);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { CardColumnGroup } from '@scm-manager/ui-components';
import { PluginGroup } from '@scm-manager/ui-types';
import PluginEntry from './PluginEntry';
import React from "react";
import { CardColumnGroup } from "@scm-manager/ui-components";
import { PluginGroup } from "@scm-manager/ui-types";
import PluginEntry from "./PluginEntry";
type Props = {
group: PluginGroup;

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { Plugin } from '@scm-manager/ui-types';
import PluginGroupEntry from '../components/PluginGroupEntry';
import groupByCategory from './groupByCategory';
import React from "react";
import { Plugin } from "@scm-manager/ui-types";
import PluginGroupEntry from "../components/PluginGroupEntry";
import groupByCategory from "./groupByCategory";
type Props = {
plugins: Plugin[];

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { translate } from 'react-i18next';
import classNames from 'classnames';
import styled from 'styled-components';
import { Plugin } from '@scm-manager/ui-types';
import React from "react";
import { translate } from "react-i18next";
import classNames from "classnames";
import styled from "styled-components";
import { Plugin } from "@scm-manager/ui-types";
import {
apiClient,
Button,
@@ -10,11 +10,11 @@ import {
Checkbox,
ErrorNotification,
Modal,
Notification,
} from '@scm-manager/ui-components';
import waitForRestart from './waitForRestart';
import SuccessNotification from './SuccessNotification';
import { PluginAction } from './PluginEntry';
Notification
} from "@scm-manager/ui-components";
import waitForRestart from "./waitForRestart";
import SuccessNotification from "./SuccessNotification";
import { PluginAction } from "./PluginEntry";
type Props = {
plugin: Plugin;
@@ -36,7 +36,7 @@ type State = {
const ListParent = styled.div`
margin-right: 0;
min-width: ${props =>
props.pluginAction === PluginAction.INSTALL ? '5.5em' : '10em'};
props.pluginAction === PluginAction.INSTALL ? "5.5em" : "10em"};
text-align: left;
`;
@@ -50,7 +50,7 @@ class PluginModal extends React.Component<Props, State> {
this.state = {
loading: false,
restart: false,
success: false,
success: false
};
}
@@ -60,7 +60,7 @@ class PluginModal extends React.Component<Props, State> {
const newState = {
loading: false,
error: undefined,
error: undefined
};
if (restart) {
@@ -68,14 +68,14 @@ class PluginModal extends React.Component<Props, State> {
.then(() => {
this.setState({
...newState,
success: true,
success: true
});
})
.catch(error => {
this.setState({
loading: false,
success: false,
error,
error
});
});
} else {
@@ -90,7 +90,7 @@ class PluginModal extends React.Component<Props, State> {
const { plugin, pluginAction } = this.props;
const { restart } = this.state;
let pluginActionLink = '';
let pluginActionLink = "";
if (pluginAction === PluginAction.INSTALL) {
pluginActionLink = plugin._links.install.href;
@@ -99,12 +99,12 @@ class PluginModal extends React.Component<Props, State> {
} else if (pluginAction === PluginAction.UNINSTALL) {
pluginActionLink = plugin._links.uninstall.href;
}
return pluginActionLink + '?restart=' + restart.toString();
return pluginActionLink + "?restart=" + restart.toString();
};
handlePluginAction = (e: Event) => {
this.setState({
loading: true,
loading: true
});
e.preventDefault();
apiClient
@@ -114,7 +114,7 @@ class PluginModal extends React.Component<Props, State> {
this.setState({
loading: false,
success: false,
error: error,
error: error
});
});
};
@@ -123,10 +123,10 @@ class PluginModal extends React.Component<Props, State> {
const { pluginAction, onClose, t } = this.props;
const { loading, error, restart, success } = this.state;
let color = pluginAction === PluginAction.UNINSTALL ? 'warning' : 'primary';
let color = pluginAction === PluginAction.UNINSTALL ? "warning" : "primary";
let label = `plugins.modal.${pluginAction}`;
if (restart) {
color = 'warning';
color = "warning";
label = `plugins.modal.${pluginAction}AndRestart`;
}
return (
@@ -138,7 +138,7 @@ class PluginModal extends React.Component<Props, State> {
loading={loading}
disabled={!!error || success}
/>
<Button label={t('plugins.modal.abort')} action={onClose} />
<Button label={t("plugins.modal.abort")} action={onClose} />
</ButtonGroup>
);
};
@@ -151,7 +151,7 @@ class PluginModal extends React.Component<Props, State> {
dependencies = (
<div className="media">
<Notification type="warning">
<strong>{t('plugins.modal.dependencyNotification')}</strong>
<strong>{t("plugins.modal.dependencyNotification")}</strong>
<ul>
{plugin.dependencies.map((dependency, index) => {
return <li key={index}>{dependency}</li>;
@@ -183,7 +183,7 @@ class PluginModal extends React.Component<Props, State> {
return (
<div className="media">
<Notification type="warning">
{t('plugins.modal.restartNotification')}
{t("plugins.modal.restartNotification")}
</Notification>
</div>
);
@@ -193,7 +193,7 @@ class PluginModal extends React.Component<Props, State> {
handleRestartChange = (value: boolean) => {
this.setState({
restart: value,
restart: value
});
};
@@ -212,25 +212,25 @@ class PluginModal extends React.Component<Props, State> {
<div className="media-content">
<div className="field is-horizontal">
<ListParent
className={classNames('field-label', 'is-inline-flex')}
className={classNames("field-label", "is-inline-flex")}
pluginAction={pluginAction}
>
{t('plugins.modal.author')}:
{t("plugins.modal.author")}:
</ListParent>
<ListChild className={classNames('field-body', 'is-inline-flex')}>
<ListChild className={classNames("field-body", "is-inline-flex")}>
{plugin.author}
</ListChild>
</div>
{pluginAction === PluginAction.INSTALL && (
<div className="field is-horizontal">
<ListParent
className={classNames('field-label', 'is-inline-flex')}
className={classNames("field-label", "is-inline-flex")}
pluginAction={pluginAction}
>
{t('plugins.modal.version')}:
{t("plugins.modal.version")}:
</ListParent>
<ListChild
className={classNames('field-body', 'is-inline-flex')}
className={classNames("field-body", "is-inline-flex")}
>
{plugin.version}
</ListChild>
@@ -240,12 +240,12 @@ class PluginModal extends React.Component<Props, State> {
pluginAction === PluginAction.UNINSTALL) && (
<div className="field is-horizontal">
<ListParent
className={classNames('field-label', 'is-inline-flex')}
className={classNames("field-label", "is-inline-flex")}
>
{t('plugins.modal.currentVersion')}:
{t("plugins.modal.currentVersion")}:
</ListParent>
<ListChild
className={classNames('field-body', 'is-inline-flex')}
className={classNames("field-body", "is-inline-flex")}
>
{plugin.version}
</ListChild>
@@ -254,12 +254,12 @@ class PluginModal extends React.Component<Props, State> {
{pluginAction === PluginAction.UPDATE && (
<div className="field is-horizontal">
<ListParent
className={classNames('field-label', 'is-inline-flex')}
className={classNames("field-label", "is-inline-flex")}
>
{t('plugins.modal.newVersion')}:
{t("plugins.modal.newVersion")}:
</ListParent>
<ListChild
className={classNames('field-body', 'is-inline-flex')}
className={classNames("field-body", "is-inline-flex")}
>
{plugin.newVersion}
</ListChild>
@@ -272,7 +272,7 @@ class PluginModal extends React.Component<Props, State> {
<div className="media-content">
<Checkbox
checked={restart}
label={t('plugins.modal.restart')}
label={t("plugins.modal.restart")}
onChange={this.handleRestartChange}
disabled={false}
/>
@@ -285,7 +285,7 @@ class PluginModal extends React.Component<Props, State> {
return (
<Modal
title={t(`plugins.modal.title.${pluginAction}`, {
name: plugin.displayName ? plugin.displayName : plugin.name,
name: plugin.displayName ? plugin.displayName : plugin.name
})}
closeFunction={() => onClose()}
body={body}
@@ -296,4 +296,4 @@ class PluginModal extends React.Component<Props, State> {
}
}
export default translate('admin')(PluginModal);
export default translate("admin")(PluginModal);

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import classNames from 'classnames';
import styled from 'styled-components';
import * as React from "react";
import classNames from "classnames";
import styled from "styled-components";
type Props = {
children?: React.Node;
@@ -17,10 +17,10 @@ export default class PluginTopActions extends React.Component<Props> {
return (
<ChildWrapper
className={classNames(
'column',
'is-flex',
'is-one-fifths',
'is-mobile-action-spacing',
"column",
"is-flex",
"is-one-fifths",
"is-mobile-action-spacing"
)}
>
{children}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Notification } from '@scm-manager/ui-components';
import React from "react";
import { translate } from "react-i18next";
import { Notification } from "@scm-manager/ui-components";
type Props = {
// context props
@@ -12,13 +12,13 @@ class InstallSuccessNotification extends React.Component<Props> {
const { t } = this.props;
return (
<Notification type="success">
{t('plugins.modal.successNotification')}{' '}
{t("plugins.modal.successNotification")}{" "}
<a onClick={e => window.location.reload(true)}>
{t('plugins.modal.reload')}
{t("plugins.modal.reload")}
</a>
</Notification>
);
}
}
export default translate('admin')(InstallSuccessNotification);
export default translate("admin")(InstallSuccessNotification);

View File

@@ -1,8 +1,8 @@
import React from 'react';
import PluginActionModal from './PluginActionModal';
import { PluginCollection } from '@scm-manager/ui-types';
import { apiClient } from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
import React from "react";
import PluginActionModal from "./PluginActionModal";
import { PluginCollection } from "@scm-manager/ui-types";
import { apiClient } from "@scm-manager/ui-components";
import { translate } from "react-i18next";
type Props = {
onClose: () => void;
@@ -19,8 +19,8 @@ class UpdateAllActionModal extends React.Component<Props> {
return (
<PluginActionModal
description={t('plugins.modal.updateAll')}
label={t('plugins.updateAll')}
description={t("plugins.modal.updateAll")}
label={t("plugins.updateAll")}
onClose={onClose}
installedPlugins={installedPlugins}
execute={this.updateAll}
@@ -37,4 +37,4 @@ class UpdateAllActionModal extends React.Component<Props> {
};
}
export default translate('admin')(UpdateAllActionModal);
export default translate("admin")(UpdateAllActionModal);

View File

@@ -1,4 +1,4 @@
import { Plugin, PluginGroup } from '@scm-manager/ui-types';
import { Plugin, PluginGroup } from "@scm-manager/ui-types";
export default function groupByCategory(plugins: Plugin[]): PluginGroup[] {
let groups = {};
@@ -9,7 +9,7 @@ export default function groupByCategory(plugins: Plugin[]): PluginGroup[] {
if (!group) {
group = {
name: groupName,
plugins: [],
plugins: []
};
groups[groupName] = group;
}

View File

@@ -1,4 +1,4 @@
import { apiClient } from '@scm-manager/ui-components';
import { apiClient } from "@scm-manager/ui-components";
const waitForRestart = () => {
const endTime = Number(new Date()) + 10000;
@@ -11,13 +11,13 @@ const waitForRestart = () => {
setTimeout(executor, 1000, resolve, reject);
} else {
apiClient
.get('')
.get("")
.then(resolve)
.catch(() => {
if (Number(new Date()) < endTime) {
setTimeout(executor, 500, resolve, reject);
} else {
reject(new Error('timeout reached'));
reject(new Error("timeout reached"));
}
});
}

View File

@@ -1,8 +1,8 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { compose } from 'redux';
import { PendingPlugins, PluginCollection } from '@scm-manager/ui-types';
import * as React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { compose } from "redux";
import { PendingPlugins, PluginCollection } from "@scm-manager/ui-types";
import {
ButtonGroup,
ErrorNotification,
@@ -10,27 +10,27 @@ import {
Notification,
Subtitle,
Title,
Button,
} from '@scm-manager/ui-components';
Button
} from "@scm-manager/ui-components";
import {
fetchPendingPlugins,
fetchPluginsByLink,
getFetchPluginsFailure,
getPendingPlugins,
getPluginCollection,
isFetchPluginsPending,
} from '../modules/plugins';
import PluginsList from '../components/PluginList';
isFetchPluginsPending
} from "../modules/plugins";
import PluginsList from "../components/PluginList";
import {
getAvailablePluginsLink,
getInstalledPluginsLink,
getPendingPluginsLink,
} from '../../../modules/indexResource';
import PluginTopActions from '../components/PluginTopActions';
import PluginBottomActions from '../components/PluginBottomActions';
import ExecutePendingActionModal from '../components/ExecutePendingActionModal';
import CancelPendingActionModal from '../components/CancelPendingActionModal';
import UpdateAllActionModal from '../components/UpdateAllActionModal';
getPendingPluginsLink
} from "../../../modules/indexResource";
import PluginTopActions from "../components/PluginTopActions";
import PluginBottomActions from "../components/PluginBottomActions";
import ExecutePendingActionModal from "../components/ExecutePendingActionModal";
import CancelPendingActionModal from "../components/CancelPendingActionModal";
import UpdateAllActionModal from "../components/UpdateAllActionModal";
type Props = {
loading: boolean;
@@ -63,7 +63,7 @@ class PluginsOverview extends React.Component<Props, State> {
this.state = {
showPendingModal: false,
showUpdateAllModal: false,
showCancelModal: false,
showCancelModal: false
};
}
@@ -85,7 +85,7 @@ class PluginsOverview extends React.Component<Props, State> {
availablePluginsLink,
installedPluginsLink,
pendingPluginsLink,
fetchPendingPlugins,
fetchPendingPlugins
} = this.props;
fetchPluginsByLink(installed ? installedPluginsLink : availablePluginsLink);
if (pendingPluginsLink) {
@@ -98,12 +98,12 @@ class PluginsOverview extends React.Component<Props, State> {
return (
<div className="columns">
<div className="column">
<Title title={t('plugins.title')} />
<Title title={t("plugins.title")} />
<Subtitle
subtitle={
installed
? t('plugins.installedSubtitle')
: t('plugins.availableSubtitle')
? t("plugins.installedSubtitle")
: t("plugins.availableSubtitle")
}
/>
</div>
@@ -132,15 +132,15 @@ class PluginsOverview extends React.Component<Props, State> {
<Button
color="primary"
reducedMobile={true}
key={'executePending'}
icon={'arrow-circle-right'}
label={t('plugins.executePending')}
key={"executePending"}
icon={"arrow-circle-right"}
label={t("plugins.executePending")}
action={() =>
this.setState({
showPendingModal: true,
showPendingModal: true
})
}
/>,
/>
);
}
@@ -153,15 +153,15 @@ class PluginsOverview extends React.Component<Props, State> {
<Button
color="primary"
reducedMobile={true}
key={'cancelPending'}
icon={'times'}
label={t('plugins.cancelPending')}
key={"cancelPending"}
icon={"times"}
label={t("plugins.cancelPending")}
action={() =>
this.setState({
showCancelModal: true,
showCancelModal: true
})
}
/>,
/>
);
}
@@ -170,15 +170,15 @@ class PluginsOverview extends React.Component<Props, State> {
<Button
color="primary"
reducedMobile={true}
key={'updateAll'}
icon={'sync-alt'}
key={"updateAll"}
icon={"sync-alt"}
label={this.computeUpdateAllSize()}
action={() =>
this.setState({
showUpdateAllModal: true,
showUpdateAllModal: true
})
}
/>,
/>
);
}
@@ -191,10 +191,10 @@ class PluginsOverview extends React.Component<Props, State> {
computeUpdateAllSize = () => {
const { collection, t } = this.props;
const outdatedPlugins = collection._embedded.plugins.filter(
p => p._links.update,
p => p._links.update
).length;
return t('plugins.outdatedPlugins', {
count: outdatedPlugins,
return t("plugins.outdatedPlugins", {
count: outdatedPlugins
});
};
@@ -226,7 +226,7 @@ class PluginsOverview extends React.Component<Props, State> {
const {
showPendingModal,
showCancelModal,
showUpdateAllModal,
showUpdateAllModal
} = this.state;
if (showPendingModal) {
@@ -234,7 +234,7 @@ class PluginsOverview extends React.Component<Props, State> {
<ExecutePendingActionModal
onClose={() =>
this.setState({
showPendingModal: false,
showPendingModal: false
})
}
pendingPlugins={pendingPlugins}
@@ -246,7 +246,7 @@ class PluginsOverview extends React.Component<Props, State> {
<CancelPendingActionModal
onClose={() =>
this.setState({
showCancelModal: false,
showCancelModal: false
})
}
refresh={this.fetchPlugins}
@@ -259,7 +259,7 @@ class PluginsOverview extends React.Component<Props, State> {
<UpdateAllActionModal
onClose={() =>
this.setState({
showUpdateAllModal: false,
showUpdateAllModal: false
})
}
refresh={this.fetchPlugins}
@@ -280,7 +280,7 @@ class PluginsOverview extends React.Component<Props, State> {
/>
);
}
return <Notification type="info">{t('plugins.noPlugins')}</Notification>;
return <Notification type="info">{t("plugins.noPlugins")}</Notification>;
}
}
@@ -300,7 +300,7 @@ const mapStateToProps = state => {
availablePluginsLink,
installedPluginsLink,
pendingPluginsLink,
pendingPlugins,
pendingPlugins
};
};
@@ -311,14 +311,14 @@ const mapDispatchToProps = dispatch => {
},
fetchPendingPlugins: (link: string) => {
dispatch(fetchPendingPlugins(link));
},
}
};
};
export default compose(
translate('admin'),
translate("admin"),
connect(
mapStateToProps,
mapDispatchToProps,
),
mapDispatchToProps
)
)(PluginsOverview);

View File

@@ -1,6 +1,6 @@
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_PLUGINS,
FETCH_PLUGINS_PENDING,
@@ -20,77 +20,77 @@ import reducer, {
fetchPluginSuccess,
getPlugin,
isFetchPluginPending,
getFetchPluginFailure,
} from './plugins';
import { Plugin, PluginCollection } from '@scm-manager/ui-types';
getFetchPluginFailure
} from "./plugins";
import { Plugin, PluginCollection } from "@scm-manager/ui-types";
const groupManagerPlugin: Plugin = {
name: 'scm-groupmanager-plugin',
bundles: ['/scm/groupmanager-plugin.bundle.js'],
type: 'Administration',
version: '2.0.0-SNAPSHOT',
author: 'Sebastian Sdorra',
description: 'Notify a remote webserver whenever a plugin is pushed to.',
name: "scm-groupmanager-plugin",
bundles: ["/scm/groupmanager-plugin.bundle.js"],
type: "Administration",
version: "2.0.0-SNAPSHOT",
author: "Sebastian Sdorra",
description: "Notify a remote webserver whenever a plugin is pushed to.",
_links: {
self: {
href: 'http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin',
},
},
href: "http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin"
}
}
};
const scriptPlugin: Plugin = {
name: 'scm-script-plugin',
bundles: ['/scm/script-plugin.bundle.js'],
type: 'Miscellaneous',
version: '2.0.0-SNAPSHOT',
author: 'Sebastian Sdorra',
description: 'Script support for scm-manager.',
name: "scm-script-plugin",
bundles: ["/scm/script-plugin.bundle.js"],
type: "Miscellaneous",
version: "2.0.0-SNAPSHOT",
author: "Sebastian Sdorra",
description: "Script support for scm-manager.",
_links: {
self: {
href: 'http://localhost:8081/api/v2/ui/plugins/scm-script-plugin',
},
},
href: "http://localhost:8081/api/v2/ui/plugins/scm-script-plugin"
}
}
};
const branchwpPlugin: Plugin = {
name: 'scm-branchwp-plugin',
bundles: ['/scm/branchwp-plugin.bundle.js'],
type: 'Miscellaneous',
version: '2.0.0-SNAPSHOT',
author: 'Sebastian Sdorra',
description: 'This plugin adds branch write protection for plugins.',
name: "scm-branchwp-plugin",
bundles: ["/scm/branchwp-plugin.bundle.js"],
type: "Miscellaneous",
version: "2.0.0-SNAPSHOT",
author: "Sebastian Sdorra",
description: "This plugin adds branch write protection for plugins.",
_links: {
self: {
href: 'http://localhost:8081/api/v2/ui/plugins/scm-branchwp-plugin',
},
},
href: "http://localhost:8081/api/v2/ui/plugins/scm-branchwp-plugin"
}
}
};
const pluginCollectionWithNames: PluginCollection = {
_links: {
self: {
href: 'http://localhost:8081/api/v2/ui/plugins',
},
href: "http://localhost:8081/api/v2/ui/plugins"
}
},
_embedded: {
plugins: [groupManagerPlugin.name, scriptPlugin.name, branchwpPlugin.name],
},
plugins: [groupManagerPlugin.name, scriptPlugin.name, branchwpPlugin.name]
}
};
const pluginCollection: PluginCollection = {
_links: {
self: {
href: 'http://localhost:8081/api/v2/ui/plugins',
},
href: "http://localhost:8081/api/v2/ui/plugins"
}
},
_embedded: {
plugins: [groupManagerPlugin, scriptPlugin, branchwpPlugin],
},
plugins: [groupManagerPlugin, scriptPlugin, branchwpPlugin]
}
};
describe('plugins fetch', () => {
const URL = 'ui/plugins';
const PLUGINS_URL = '/api/v2/ui/plugins';
describe("plugins fetch", () => {
const URL = "ui/plugins";
const PLUGINS_URL = "/api/v2/ui/plugins";
const mockStore = configureMockStore([thunk]);
afterEach(() => {
@@ -98,17 +98,17 @@ describe('plugins fetch', () => {
fetchMock.restore();
});
it('should successfully fetch plugins from link', () => {
it("should successfully fetch plugins from link", () => {
fetchMock.getOnce(PLUGINS_URL, pluginCollection);
const expectedActions = [
{
type: FETCH_PLUGINS_PENDING,
type: FETCH_PLUGINS_PENDING
},
{
type: FETCH_PLUGINS_SUCCESS,
payload: pluginCollection,
},
payload: pluginCollection
}
];
const store = mockStore({});
@@ -117,9 +117,9 @@ describe('plugins fetch', () => {
});
});
it('should dispatch FETCH_PLUGINS_FAILURE if request fails', () => {
it("should dispatch FETCH_PLUGINS_FAILURE if request fails", () => {
fetchMock.getOnce(PLUGINS_URL, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -131,72 +131,72 @@ describe('plugins fetch', () => {
});
});
it('should successfully fetch scm-groupmanager-plugin by name', () => {
it("should successfully fetch scm-groupmanager-plugin by name", () => {
fetchMock.getOnce(
PLUGINS_URL + '/scm-groupmanager-plugin',
groupManagerPlugin,
PLUGINS_URL + "/scm-groupmanager-plugin",
groupManagerPlugin
);
const expectedActions = [
{
type: FETCH_PLUGIN_PENDING,
payload: {
name: 'scm-groupmanager-plugin',
name: "scm-groupmanager-plugin"
},
itemId: 'scm-groupmanager-plugin',
itemId: "scm-groupmanager-plugin"
},
{
type: FETCH_PLUGIN_SUCCESS,
payload: groupManagerPlugin,
itemId: 'scm-groupmanager-plugin',
},
itemId: "scm-groupmanager-plugin"
}
];
const store = mockStore({});
return store
.dispatch(fetchPluginByName(URL, 'scm-groupmanager-plugin'))
.dispatch(fetchPluginByName(URL, "scm-groupmanager-plugin"))
.then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
it('should dispatch FETCH_PLUGIN_FAILURE, if the request for scm-groupmanager-plugin by name fails', () => {
fetchMock.getOnce(PLUGINS_URL + '/scm-groupmanager-plugin', {
status: 500,
it("should dispatch FETCH_PLUGIN_FAILURE, if the request for scm-groupmanager-plugin by name fails", () => {
fetchMock.getOnce(PLUGINS_URL + "/scm-groupmanager-plugin", {
status: 500
});
const store = mockStore({});
return store
.dispatch(fetchPluginByName(URL, 'scm-groupmanager-plugin'))
.dispatch(fetchPluginByName(URL, "scm-groupmanager-plugin"))
.then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_PLUGIN_PENDING);
expect(actions[1].type).toEqual(FETCH_PLUGIN_FAILURE);
expect(actions[1].payload.name).toBe('scm-groupmanager-plugin');
expect(actions[1].payload.name).toBe("scm-groupmanager-plugin");
expect(actions[1].payload.error).toBeDefined();
expect(actions[1].itemId).toBe('scm-groupmanager-plugin');
expect(actions[1].itemId).toBe("scm-groupmanager-plugin");
});
});
it('should successfully fetch scm-groupmanager-plugin', () => {
it("should successfully fetch scm-groupmanager-plugin", () => {
fetchMock.getOnce(
'http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin',
groupManagerPlugin,
"http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin",
groupManagerPlugin
);
const expectedActions = [
{
type: FETCH_PLUGIN_PENDING,
payload: {
name: 'scm-groupmanager-plugin',
name: "scm-groupmanager-plugin"
},
itemId: 'scm-groupmanager-plugin',
itemId: "scm-groupmanager-plugin"
},
{
type: FETCH_PLUGIN_SUCCESS,
payload: groupManagerPlugin,
itemId: 'scm-groupmanager-plugin',
},
itemId: "scm-groupmanager-plugin"
}
];
const store = mockStore({});
@@ -205,12 +205,12 @@ describe('plugins fetch', () => {
});
});
it('should dispatch FETCH_PLUGIN_FAILURE, it the request for scm-groupmanager-plugin fails', () => {
it("should dispatch FETCH_PLUGIN_FAILURE, it the request for scm-groupmanager-plugin fails", () => {
fetchMock.getOnce(
'http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin',
"http://localhost:8081/api/v2/ui/plugins/scm-groupmanager-plugin",
{
status: 500,
},
status: 500
}
);
const store = mockStore({});
@@ -218,145 +218,145 @@ describe('plugins fetch', () => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_PLUGIN_PENDING);
expect(actions[1].type).toEqual(FETCH_PLUGIN_FAILURE);
expect(actions[1].payload.name).toBe('scm-groupmanager-plugin');
expect(actions[1].payload.name).toBe("scm-groupmanager-plugin");
expect(actions[1].payload.error).toBeDefined();
expect(actions[1].itemId).toBe('scm-groupmanager-plugin');
expect(actions[1].itemId).toBe("scm-groupmanager-plugin");
});
});
});
describe('plugins reducer', () => {
it('should return empty object, if state and action is undefined', () => {
describe("plugins reducer", () => {
it("should return empty object, if state and action is undefined", () => {
expect(reducer()).toEqual({});
});
it('should return the same state, if the action is undefined', () => {
it("should return the same state, if the action is undefined", () => {
const state = {
x: true,
x: true
};
expect(reducer(state)).toBe(state);
});
it('should return the same state, if the action is unknown to the reducer', () => {
it("should return the same state, if the action is unknown to the reducer", () => {
const state = {
x: true,
x: true
};
expect(
reducer(state, {
type: 'EL_SPECIALE',
}),
type: "EL_SPECIALE"
})
).toBe(state);
});
it("should store the plugins by it's type and name on FETCH_PLUGINS_SUCCESS", () => {
const newState = reducer({}, fetchPluginsSuccess(pluginCollection));
expect(newState.list._embedded.plugins).toEqual([
'scm-groupmanager-plugin',
'scm-script-plugin',
'scm-branchwp-plugin',
"scm-groupmanager-plugin",
"scm-script-plugin",
"scm-branchwp-plugin"
]);
expect(newState.byNames['scm-groupmanager-plugin']).toBe(
groupManagerPlugin,
expect(newState.byNames["scm-groupmanager-plugin"]).toBe(
groupManagerPlugin
);
expect(newState.byNames['scm-script-plugin']).toBe(scriptPlugin);
expect(newState.byNames['scm-branchwp-plugin']).toBe(branchwpPlugin);
expect(newState.byNames["scm-script-plugin"]).toBe(scriptPlugin);
expect(newState.byNames["scm-branchwp-plugin"]).toBe(branchwpPlugin);
});
it('should store the plugin at byNames', () => {
it("should store the plugin at byNames", () => {
const newState = reducer({}, fetchPluginSuccess(groupManagerPlugin));
expect(newState.byNames['scm-groupmanager-plugin']).toBe(
groupManagerPlugin,
expect(newState.byNames["scm-groupmanager-plugin"]).toBe(
groupManagerPlugin
);
});
});
describe('plugins selectors', () => {
const error = new Error('something went wrong');
describe("plugins selectors", () => {
const error = new Error("something went wrong");
it('should return the plugins collection', () => {
it("should return the plugins collection", () => {
const state = {
plugins: {
list: pluginCollectionWithNames,
byNames: {
'scm-groupmanager-plugin': groupManagerPlugin,
'scm-script-plugin': scriptPlugin,
'scm-branchwp-plugin': branchwpPlugin,
},
},
"scm-groupmanager-plugin": groupManagerPlugin,
"scm-script-plugin": scriptPlugin,
"scm-branchwp-plugin": branchwpPlugin
}
}
};
const collection = getPluginCollection(state);
expect(collection).toEqual(pluginCollection);
});
it('should return true, when fetch plugins is pending', () => {
it("should return true, when fetch plugins is pending", () => {
const state = {
pending: {
[FETCH_PLUGINS]: true,
},
[FETCH_PLUGINS]: true
}
};
expect(isFetchPluginsPending(state)).toEqual(true);
});
it('should return false, when fetch plugins is not pending', () => {
it("should return false, when fetch plugins is not pending", () => {
expect(isFetchPluginsPending({})).toEqual(false);
});
it('should return error when fetch plugins did fail', () => {
it("should return error when fetch plugins did fail", () => {
const state = {
failure: {
[FETCH_PLUGINS]: error,
},
[FETCH_PLUGINS]: error
}
};
expect(getFetchPluginsFailure(state)).toEqual(error);
});
it('should return undefined when fetch plugins did not fail', () => {
it("should return undefined when fetch plugins did not fail", () => {
expect(getFetchPluginsFailure({})).toBe(undefined);
});
it('should return the plugin collection', () => {
it("should return the plugin collection", () => {
const state = {
plugins: {
byNames: {
'scm-groupmanager-plugin': groupManagerPlugin,
},
},
"scm-groupmanager-plugin": groupManagerPlugin
}
}
};
const plugin = getPlugin(state, 'scm-groupmanager-plugin');
const plugin = getPlugin(state, "scm-groupmanager-plugin");
expect(plugin).toEqual(groupManagerPlugin);
});
it('should return true, when fetch plugin is pending', () => {
it("should return true, when fetch plugin is pending", () => {
const state = {
pending: {
[FETCH_PLUGIN + '/scm-groupmanager-plugin']: true,
},
[FETCH_PLUGIN + "/scm-groupmanager-plugin"]: true
}
};
expect(isFetchPluginPending(state, 'scm-groupmanager-plugin')).toEqual(
true,
expect(isFetchPluginPending(state, "scm-groupmanager-plugin")).toEqual(
true
);
});
it('should return false, when fetch plugin is not pending', () => {
expect(isFetchPluginPending({}, 'scm-groupmanager-plugin')).toEqual(false);
it("should return false, when fetch plugin is not pending", () => {
expect(isFetchPluginPending({}, "scm-groupmanager-plugin")).toEqual(false);
});
it('should return error when fetch plugin did fail', () => {
it("should return error when fetch plugin did fail", () => {
const state = {
failure: {
[FETCH_PLUGIN + '/scm-groupmanager-plugin']: error,
},
[FETCH_PLUGIN + "/scm-groupmanager-plugin"]: error
}
};
expect(getFetchPluginFailure(state, 'scm-groupmanager-plugin')).toEqual(
error,
expect(getFetchPluginFailure(state, "scm-groupmanager-plugin")).toEqual(
error
);
});
it('should return undefined when fetch plugin did not fail', () => {
expect(getFetchPluginFailure({}, 'scm-groupmanager-plugin')).toBe(
undefined,
it("should return undefined when fetch plugin did not fail", () => {
expect(getFetchPluginFailure({}, "scm-groupmanager-plugin")).toBe(
undefined
);
});
});

View File

@@ -1,20 +1,20 @@
import * as types from '../../../modules/types';
import { isPending } from '../../../modules/pending';
import { getFailure } from '../../../modules/failure';
import { Action, Plugin, PluginCollection } from '@scm-manager/ui-types';
import { apiClient } from '@scm-manager/ui-components';
import * as types from "../../../modules/types";
import { isPending } from "../../../modules/pending";
import { getFailure } from "../../../modules/failure";
import { Action, Plugin, PluginCollection } from "@scm-manager/ui-types";
import { apiClient } from "@scm-manager/ui-components";
export const FETCH_PLUGINS = 'scm/plugins/FETCH_PLUGINS';
export const FETCH_PLUGINS = "scm/plugins/FETCH_PLUGINS";
export const FETCH_PLUGINS_PENDING = `${FETCH_PLUGINS}_${types.PENDING_SUFFIX}`;
export const FETCH_PLUGINS_SUCCESS = `${FETCH_PLUGINS}_${types.SUCCESS_SUFFIX}`;
export const FETCH_PLUGINS_FAILURE = `${FETCH_PLUGINS}_${types.FAILURE_SUFFIX}`;
export const FETCH_PLUGIN = 'scm/plugins/FETCH_PLUGIN';
export const FETCH_PLUGIN = "scm/plugins/FETCH_PLUGIN";
export const FETCH_PLUGIN_PENDING = `${FETCH_PLUGIN}_${types.PENDING_SUFFIX}`;
export const FETCH_PLUGIN_SUCCESS = `${FETCH_PLUGIN}_${types.SUCCESS_SUFFIX}`;
export const FETCH_PLUGIN_FAILURE = `${FETCH_PLUGIN}_${types.FAILURE_SUFFIX}`;
export const FETCH_PENDING_PLUGINS = 'scm/plugins/FETCH_PENDING_PLUGINS';
export const FETCH_PENDING_PLUGINS = "scm/plugins/FETCH_PENDING_PLUGINS";
export const FETCH_PENDING_PLUGINS_PENDING = `${FETCH_PENDING_PLUGINS}_${types.PENDING_SUFFIX}`;
export const FETCH_PENDING_PLUGINS_SUCCESS = `${FETCH_PENDING_PLUGINS}_${types.SUCCESS_SUFFIX}`;
export const FETCH_PENDING_PLUGINS_FAILURE = `${FETCH_PENDING_PLUGINS}_${types.FAILURE_SUFFIX}`;
@@ -37,21 +37,21 @@ export function fetchPluginsByLink(link: string) {
export function fetchPluginsPending(): Action {
return {
type: FETCH_PLUGINS_PENDING,
type: FETCH_PLUGINS_PENDING
};
}
export function fetchPluginsSuccess(plugins: PluginCollection): Action {
return {
type: FETCH_PLUGINS_SUCCESS,
payload: plugins,
payload: plugins
};
}
export function fetchPluginsFailure(err: Error): Action {
return {
type: FETCH_PLUGINS_FAILURE,
payload: err,
payload: err
};
}
@@ -61,7 +61,7 @@ export function fetchPluginByLink(plugin: Plugin) {
}
export function fetchPluginByName(link: string, name: string) {
const pluginUrl = link.endsWith('/') ? link : link + '/';
const pluginUrl = link.endsWith("/") ? link : link + "/";
return fetchPlugin(pluginUrl + name, name);
}
@@ -84,9 +84,9 @@ export function fetchPluginPending(name: string): Action {
return {
type: FETCH_PLUGIN_PENDING,
payload: {
name,
name
},
itemId: name,
itemId: name
};
}
@@ -94,7 +94,7 @@ export function fetchPluginSuccess(plugin: Plugin): Action {
return {
type: FETCH_PLUGIN_SUCCESS,
payload: plugin,
itemId: plugin.name,
itemId: plugin.name
};
}
@@ -103,9 +103,9 @@ export function fetchPluginFailure(name: string, error: Error): Action {
type: FETCH_PLUGIN_FAILURE,
payload: {
name,
error,
error
},
itemId: name,
itemId: name
};
}
@@ -127,21 +127,21 @@ export function fetchPendingPlugins(link: string) {
export function fetchPendingPluginsPending(): Action {
return {
type: FETCH_PENDING_PLUGINS_PENDING,
type: FETCH_PENDING_PLUGINS_PENDING
};
}
export function fetchPendingPluginsSuccess(PendingPlugins: {}): Action {
return {
type: FETCH_PENDING_PLUGINS_SUCCESS,
payload: PendingPlugins,
payload: PendingPlugins
};
}
export function fetchPendingPluginsFailure(err: Error): Action {
return {
type: FETCH_PENDING_PLUGINS_FAILURE,
payload: err,
payload: err
};
}
@@ -158,10 +158,10 @@ function normalizeByName(state: object, pluginCollection: PluginCollection) {
list: {
...pluginCollection,
_embedded: {
plugins: names,
},
plugins: names
}
},
byNames: byNames,
byNames: byNames
};
}
@@ -170,16 +170,16 @@ const reducerByNames = (state: object, plugin: Plugin) => {
...state,
byNames: {
...state.byNames,
[plugin.name]: plugin,
},
[plugin.name]: plugin
}
};
};
export default function reducer(
state: object = {},
action: Action = {
type: 'UNKNOWN',
},
type: "UNKNOWN"
}
): object {
if (!action.payload) {
return state;
@@ -193,7 +193,7 @@ export default function reducer(
case FETCH_PENDING_PLUGINS_SUCCESS:
return {
...state,
pending: action.payload,
pending: action.payload
};
default:
return state;
@@ -210,8 +210,8 @@ export function getPluginCollection(state: object) {
return {
...state.plugins.list,
_embedded: {
plugins,
},
plugins
}
};
}
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { translate } from 'react-i18next';
import { RepositoryRole } from '@scm-manager/ui-types';
import React from "react";
import { translate } from "react-i18next";
import { RepositoryRole } from "@scm-manager/ui-types";
type Props = {
role: RepositoryRole;
@@ -21,7 +21,7 @@ class AvailableVerbs extends React.Component<Props> {
<ul>
{role.verbs.map(verb => {
return (
<li>{t('verbs.repository.' + verb + '.displayName')}</li>
<li>{t("verbs.repository." + verb + ".displayName")}</li>
);
})}
</ul>
@@ -33,4 +33,4 @@ class AvailableVerbs extends React.Component<Props> {
}
}
export default translate('plugins')(AvailableVerbs);
export default translate("plugins")(AvailableVerbs);

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { translate } from 'react-i18next';
import { RepositoryRole } from '@scm-manager/ui-types';
import { ExtensionPoint } from '@scm-manager/ui-extensions';
import PermissionRoleDetailsTable from './PermissionRoleDetailsTable';
import { Button } from '@scm-manager/ui-components';
import React from "react";
import { translate } from "react-i18next";
import { RepositoryRole } from "@scm-manager/ui-types";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import PermissionRoleDetailsTable from "./PermissionRoleDetailsTable";
import { Button } from "@scm-manager/ui-components";
type Props = {
role: RepositoryRole;
@@ -19,7 +19,7 @@ class PermissionRoleDetails extends React.Component<Props> {
if (!!this.props.role._links.update) {
return (
<Button
label={t('repositoryRole.editButton')}
label={t("repositoryRole.editButton")}
link={`${url}/edit`}
color="primary"
/>
@@ -40,7 +40,7 @@ class PermissionRoleDetails extends React.Component<Props> {
name="repositoryRole.role-details.information"
renderAll={true}
props={{
role,
role
}}
/>
</>
@@ -48,4 +48,4 @@ class PermissionRoleDetails extends React.Component<Props> {
}
}
export default translate('admin')(PermissionRoleDetails);
export default translate("admin")(PermissionRoleDetails);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { RepositoryRole } from '@scm-manager/ui-types';
import { translate } from 'react-i18next';
import AvailableVerbs from './AvailableVerbs';
import React from "react";
import { RepositoryRole } from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import AvailableVerbs from "./AvailableVerbs";
type Props = {
role: RepositoryRole;
@@ -17,15 +17,15 @@ class PermissionRoleDetailsTable extends React.Component<Props> {
<table className="table content">
<tbody>
<tr>
<th>{t('repositoryRole.name')}</th>
<th>{t("repositoryRole.name")}</th>
<td>{role.name}</td>
</tr>
<tr>
<th>{t('repositoryRole.type')}</th>
<th>{t("repositoryRole.type")}</th>
<td>{role.type}</td>
</tr>
<tr>
<th>{t('repositoryRole.verbs')}</th>
<th>{t("repositoryRole.verbs")}</th>
<AvailableVerbs role={role} />
</tr>
</tbody>
@@ -34,4 +34,4 @@ class PermissionRoleDetailsTable extends React.Component<Props> {
}
}
export default translate('admin')(PermissionRoleDetailsTable);
export default translate("admin")(PermissionRoleDetailsTable);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { RepositoryRole } from '@scm-manager/ui-types';
import SystemRoleTag from './SystemRoleTag';
import React from "react";
import { Link } from "react-router-dom";
import { RepositoryRole } from "@scm-manager/ui-types";
import SystemRoleTag from "./SystemRoleTag";
type Props = {
baseUrl: string;

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { translate } from 'react-i18next';
import { RepositoryRole } from '@scm-manager/ui-types';
import PermissionRoleRow from './PermissionRoleRow';
import React from "react";
import { translate } from "react-i18next";
import { RepositoryRole } from "@scm-manager/ui-types";
import PermissionRoleRow from "./PermissionRoleRow";
type Props = {
baseUrl: string;
@@ -18,7 +18,7 @@ class PermissionRoleTable extends React.Component<Props> {
<table className="card-table table is-hoverable is-fullwidth">
<thead>
<tr>
<th>{t('repositoryRole.name')}</th>
<th>{t("repositoryRole.name")}</th>
</tr>
</thead>
<tbody>
@@ -33,4 +33,4 @@ class PermissionRoleTable extends React.Component<Props> {
}
}
export default translate('admin')(PermissionRoleTable);
export default translate("admin")(PermissionRoleTable);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { translate } from 'react-i18next';
import styled from 'styled-components';
import { Tag } from '@scm-manager/ui-components';
import React from "react";
import { translate } from "react-i18next";
import styled from "styled-components";
import { Tag } from "@scm-manager/ui-components";
type Props = {
system?: boolean;
@@ -20,10 +20,10 @@ class SystemRoleTag extends React.Component<Props> {
const { system, t } = this.props;
if (system) {
return <LeftMarginTag color="dark" label={t('repositoryRole.system')} />;
return <LeftMarginTag color="dark" label={t("repositoryRole.system")} />;
}
return null;
}
}
export default translate('admin')(SystemRoleTag);
export default translate("admin")(SystemRoleTag);

View File

@@ -1,20 +1,20 @@
import React from 'react';
import RepositoryRoleForm from './RepositoryRoleForm';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { ErrorNotification, Subtitle, Title } from '@scm-manager/ui-components';
import React from "react";
import RepositoryRoleForm from "./RepositoryRoleForm";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { ErrorNotification, Subtitle, Title } from "@scm-manager/ui-components";
import {
createRole,
getCreateRoleFailure,
getFetchVerbsFailure,
isFetchVerbsPending,
} from '../modules/roles';
import { RepositoryRole } from '@scm-manager/ui-types';
isFetchVerbsPending
} from "../modules/roles";
import { RepositoryRole } from "@scm-manager/ui-types";
import {
getRepositoryRolesLink,
getRepositoryVerbsLink,
} from '../../../modules/indexResource';
import { History } from 'history';
getRepositoryVerbsLink
} from "../../../modules/indexResource";
import { History } from "history";
type Props = {
repositoryRolesLink: string;
@@ -31,12 +31,12 @@ type Props = {
class CreateRepositoryRole extends React.Component<Props> {
repositoryRoleCreated = (role: RepositoryRole) => {
const { history } = this.props;
history.push('/admin/role/' + role.name + '/info');
history.push("/admin/role/" + role.name + "/info");
};
createRepositoryRole = (role: RepositoryRole) => {
this.props.addRole(this.props.repositoryRolesLink, role, () =>
this.repositoryRoleCreated(role),
this.repositoryRoleCreated(role)
);
};
@@ -49,8 +49,8 @@ class CreateRepositoryRole extends React.Component<Props> {
return (
<>
<Title title={t('repositoryRole.title')} />
<Subtitle subtitle={t('repositoryRole.createSubtitle')} />
<Title title={t("repositoryRole.title")} />
<Subtitle subtitle={t("repositoryRole.createSubtitle")} />
<RepositoryRoleForm
submitForm={role => this.createRepositoryRole(role)}
/>
@@ -69,7 +69,7 @@ const mapStateToProps = (state, ownProps) => {
loading,
error,
verbsLink,
repositoryRolesLink,
repositoryRolesLink
};
};
@@ -77,11 +77,11 @@ const mapDispatchToProps = dispatch => {
return {
addRole: (link: string, role: RepositoryRole, callback?: () => void) => {
dispatch(createRole(link, role, callback));
},
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(translate('admin')(CreateRepositoryRole));
mapDispatchToProps
)(translate("admin")(CreateRepositoryRole));

View File

@@ -1,20 +1,20 @@
import React from 'react';
import { translate } from 'react-i18next';
import { RepositoryRole } from '@scm-manager/ui-types';
import React from "react";
import { translate } from "react-i18next";
import { RepositoryRole } from "@scm-manager/ui-types";
import {
Subtitle,
DeleteButton,
confirmAlert,
ErrorNotification,
} from '@scm-manager/ui-components';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { History } from 'history';
ErrorNotification
} from "@scm-manager/ui-components";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import { History } from "history";
import {
deleteRole,
getDeleteRoleFailure,
isDeleteRolePending,
} from '../modules/roles';
isDeleteRolePending
} from "../modules/roles";
type Props = {
loading: boolean;
@@ -30,11 +30,11 @@ type Props = {
class DeleteRepositoryRole extends React.Component<Props> {
static defaultProps = {
confirmDialog: true,
confirmDialog: true
};
roleDeleted = () => {
this.props.history.push('/admin/roles/');
this.props.history.push("/admin/roles/");
};
deleteRole = () => {
@@ -44,18 +44,18 @@ class DeleteRepositoryRole extends React.Component<Props> {
confirmDelete = () => {
const { t } = this.props;
confirmAlert({
title: t('repositoryRole.delete.confirmAlert.title'),
message: t('repositoryRole.delete.confirmAlert.message'),
title: t("repositoryRole.delete.confirmAlert.title"),
message: t("repositoryRole.delete.confirmAlert.message"),
buttons: [
{
label: t('repositoryRole.delete.confirmAlert.submit'),
onClick: () => this.deleteRole(),
label: t("repositoryRole.delete.confirmAlert.submit"),
onClick: () => this.deleteRole()
},
{
label: t('repositoryRole.delete.confirmAlert.cancel'),
onClick: () => null,
},
],
label: t("repositoryRole.delete.confirmAlert.cancel"),
onClick: () => null
}
]
});
};
@@ -73,12 +73,12 @@ class DeleteRepositoryRole extends React.Component<Props> {
return (
<>
<Subtitle subtitle={t('repositoryRole.delete.subtitle')} />
<Subtitle subtitle={t("repositoryRole.delete.subtitle")} />
<div className="columns">
<div className="column">
<ErrorNotification error={error} />
<DeleteButton
label={t('repositoryRole.delete.button')}
label={t("repositoryRole.delete.button")}
action={action}
loading={loading}
/>
@@ -94,7 +94,7 @@ const mapStateToProps = (state, ownProps) => {
const error = getDeleteRoleFailure(state, ownProps.role.name);
return {
loading,
error,
error
};
};
@@ -102,11 +102,11 @@ const mapDispatchToProps = dispatch => {
return {
deleteRole: (role: RepositoryRole, callback?: () => void) => {
dispatch(deleteRole(role, callback));
},
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(withRouter(translate('admin')(DeleteRepositoryRole)));
mapDispatchToProps
)(withRouter(translate("admin")(DeleteRepositoryRole)));

View File

@@ -1,16 +1,16 @@
import React from 'react';
import RepositoryRoleForm from './RepositoryRoleForm';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import React from "react";
import RepositoryRoleForm from "./RepositoryRoleForm";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import {
getModifyRoleFailure,
isModifyRolePending,
modifyRole,
} from '../modules/roles';
import { ErrorNotification, Subtitle } from '@scm-manager/ui-components';
import { RepositoryRole } from '@scm-manager/ui-types';
import { History } from 'history';
import DeleteRepositoryRole from './DeleteRepositoryRole';
modifyRole
} from "../modules/roles";
import { ErrorNotification, Subtitle } from "@scm-manager/ui-components";
import { RepositoryRole } from "@scm-manager/ui-types";
import { History } from "history";
import DeleteRepositoryRole from "./DeleteRepositoryRole";
type Props = {
disabled: boolean;
@@ -28,7 +28,7 @@ type Props = {
class EditRepositoryRole extends React.Component<Props> {
repositoryRoleUpdated = () => {
this.props.history.push('/admin/roles/');
this.props.history.push("/admin/roles/");
};
updateRepositoryRole = (role: RepositoryRole) => {
@@ -44,7 +44,7 @@ class EditRepositoryRole extends React.Component<Props> {
return (
<>
<Subtitle subtitle={t('repositoryRole.editSubtitle')} />
<Subtitle subtitle={t("repositoryRole.editSubtitle")} />
<RepositoryRoleForm
role={this.props.role}
submitForm={role => this.updateRepositoryRole(role)}
@@ -62,7 +62,7 @@ const mapStateToProps = (state, ownProps) => {
return {
loading,
error,
error
};
};
@@ -70,11 +70,11 @@ const mapDispatchToProps = dispatch => {
return {
updateRole: (role: RepositoryRole, callback?: () => void) => {
dispatch(modifyRole(role, callback));
},
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(translate('admin')(EditRepositoryRole));
mapDispatchToProps
)(translate("admin")(EditRepositoryRole));

View File

@@ -1,19 +1,19 @@
import React from 'react';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { RepositoryRole } from '@scm-manager/ui-types';
import { InputField, SubmitButton } from '@scm-manager/ui-components';
import PermissionCheckbox from '../../../repos/permissions/components/PermissionCheckbox';
import React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { RepositoryRole } from "@scm-manager/ui-types";
import { InputField, SubmitButton } from "@scm-manager/ui-components";
import PermissionCheckbox from "../../../repos/permissions/components/PermissionCheckbox";
import {
fetchAvailableVerbs,
getFetchVerbsFailure,
getVerbsFromState,
isFetchVerbsPending,
} from '../modules/roles';
isFetchVerbsPending
} from "../modules/roles";
import {
getRepositoryRolesLink,
getRepositoryVerbsLink,
} from '../../../modules/indexResource';
getRepositoryVerbsLink
} from "../../../modules/indexResource";
type Props = {
role?: RepositoryRole;
@@ -39,11 +39,11 @@ class RepositoryRoleForm extends React.Component<Props, State> {
this.state = {
role: {
name: '',
name: "",
verbs: [],
system: false,
_links: {},
},
_links: {}
}
};
}
@@ -52,7 +52,7 @@ class RepositoryRoleForm extends React.Component<Props, State> {
fetchAvailableVerbs(verbsLink);
if (this.props.role) {
this.setState({
role: this.props.role,
role: this.props.role
});
}
}
@@ -74,8 +74,8 @@ class RepositoryRoleForm extends React.Component<Props, State> {
this.setState({
role: {
...this.state.role,
name,
},
name
}
});
};
@@ -90,8 +90,8 @@ class RepositoryRoleForm extends React.Component<Props, State> {
...this.state,
role: {
...role,
verbs: newVerbs,
},
verbs: newVerbs
}
});
};
@@ -121,21 +121,21 @@ class RepositoryRoleForm extends React.Component<Props, State> {
<form onSubmit={this.submit}>
<InputField
name="name"
label={t('repositoryRole.form.name')}
label={t("repositoryRole.form.name")}
onChange={this.handleNameChange}
value={role.name ? role.name : ''}
value={role.name ? role.name : ""}
disabled={!!this.props.role}
/>
<div className="field">
<label className="label">
{t('repositoryRole.form.permissions')}
{t("repositoryRole.form.permissions")}
</label>
{verbSelectBoxes}
</div>
<hr />
<SubmitButton
loading={loading}
label={t('repositoryRole.form.submit')}
label={t("repositoryRole.form.submit")}
disabled={!this.isValid()}
/>
</form>
@@ -155,7 +155,7 @@ const mapStateToProps = (state, ownProps) => {
error,
verbsLink,
availableVerbs,
repositoryRolesLink,
repositoryRolesLink
};
};
@@ -163,11 +163,11 @@ const mapDispatchToProps = dispatch => {
return {
fetchAvailableVerbs: (link: string) => {
dispatch(fetchAvailableVerbs(link));
},
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(translate('admin')(RepositoryRoleForm));
mapDispatchToProps
)(translate("admin")(RepositoryRoleForm));

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { translate } from 'react-i18next';
import { History } from 'history';
import { RepositoryRole, PagedCollection } from '@scm-manager/ui-types';
import React from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import { translate } from "react-i18next";
import { History } from "history";
import { RepositoryRole, PagedCollection } from "@scm-manager/ui-types";
import {
Title,
Subtitle,
@@ -11,18 +11,18 @@ import {
Notification,
LinkPaginator,
urls,
CreateButton,
} from '@scm-manager/ui-components';
CreateButton
} from "@scm-manager/ui-components";
import {
fetchRolesByPage,
getRolesFromState,
selectListAsCollection,
isPermittedToCreateRoles,
isFetchRolesPending,
getFetchRolesFailure,
} from '../modules/roles';
import PermissionRoleTable from '../components/PermissionRoleTable';
import { getRepositoryRolesLink } from '../../../modules/indexResource';
getFetchRolesFailure
} from "../modules/roles";
import PermissionRoleTable from "../components/PermissionRoleTable";
import { getRepositoryRolesLink } from "../../../modules/indexResource";
type Props = {
baseUrl: string;
@@ -56,7 +56,7 @@ class RepositoryRoles extends React.Component<Props> {
page,
rolesLink,
location,
fetchRolesByPage,
fetchRolesByPage
} = this.props;
if (list && page && !loading) {
const statePage: number = list.page + 1;
@@ -75,8 +75,8 @@ class RepositoryRoles extends React.Component<Props> {
return (
<>
<Title title={t('repositoryRole.title')} />
<Subtitle subtitle={t('repositoryRole.overview.title')} />
<Title title={t("repositoryRole.title")} />
<Subtitle subtitle={t("repositoryRole.overview.title")} />
{this.renderPermissionsTable()}
{this.renderCreateButton()}
</>
@@ -95,7 +95,7 @@ class RepositoryRoles extends React.Component<Props> {
}
return (
<Notification type="info">
{t('repositoryRole.overview.noPermissionRoles')}
{t("repositoryRole.overview.noPermissionRoles")}
</Notification>
);
}
@@ -105,7 +105,7 @@ class RepositoryRoles extends React.Component<Props> {
if (canAddRoles) {
return (
<CreateButton
label={t('repositoryRole.overview.createButton')}
label={t("repositoryRole.overview.createButton")}
link={`${baseUrl}/create`}
/>
);
@@ -131,7 +131,7 @@ const mapStateToProps = (state, ownProps) => {
canAddRoles,
list,
page,
rolesLink,
rolesLink
};
};
@@ -139,13 +139,13 @@ const mapDispatchToProps = dispatch => {
return {
fetchRolesByPage: (link: string, page: number) => {
dispatch(fetchRolesByPage(link, page));
},
}
};
};
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(translate('admin')(RepositoryRoles)),
mapDispatchToProps
)(translate("admin")(RepositoryRoles))
);

View File

@@ -1,21 +1,21 @@
import React from 'react';
import { connect } from 'react-redux';
import { Loading, ErrorPage, Title } from '@scm-manager/ui-components';
import { Route } from 'react-router-dom';
import { History } from 'history';
import { translate } from 'react-i18next';
import { RepositoryRole } from '@scm-manager/ui-types';
import { getRepositoryRolesLink } from '../../../modules/indexResource';
import { ExtensionPoint } from '@scm-manager/ui-extensions';
import React from "react";
import { connect } from "react-redux";
import { Loading, ErrorPage, Title } from "@scm-manager/ui-components";
import { Route } from "react-router-dom";
import { History } from "history";
import { translate } from "react-i18next";
import { RepositoryRole } from "@scm-manager/ui-types";
import { getRepositoryRolesLink } from "../../../modules/indexResource";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import {
fetchRoleByName,
getFetchRoleFailure,
getRoleByName,
isFetchRolePending,
} from '../modules/roles';
import { withRouter } from 'react-router-dom';
import PermissionRoleDetail from '../components/PermissionRoleDetails';
import EditRepositoryRole from './EditRepositoryRole';
isFetchRolePending
} from "../modules/roles";
import { withRouter } from "react-router-dom";
import PermissionRoleDetail from "../components/PermissionRoleDetails";
import EditRepositoryRole from "./EditRepositoryRole";
type Props = {
roleName: string;
@@ -38,12 +38,12 @@ class SingleRepositoryRole extends React.Component<Props> {
componentDidMount() {
this.props.fetchRoleByName(
this.props.repositoryRolesLink,
this.props.roleName,
this.props.roleName
);
}
stripEndingSlash = (url: string) => {
if (url.endsWith('/')) {
if (url.endsWith("/")) {
return url.substring(0, url.length - 2);
}
return url;
@@ -59,8 +59,8 @@ class SingleRepositoryRole extends React.Component<Props> {
if (error) {
return (
<ErrorPage
title={t('repositoryRole.errorTitle')}
subtitle={t('repositoryRole.errorSubtitle')}
title={t("repositoryRole.errorTitle")}
subtitle={t("repositoryRole.errorSubtitle")}
error={error}
/>
);
@@ -74,12 +74,12 @@ class SingleRepositoryRole extends React.Component<Props> {
const extensionProps = {
role,
url,
url
};
return (
<>
<Title title={t('repositoryRole.title')} />
<Title title={t("repositoryRole.title")} />
<Route
path={`${url}/info`}
component={() => <PermissionRoleDetail role={role} url={url} />}
@@ -112,7 +112,7 @@ const mapStateToProps = (state, ownProps) => {
roleName,
role,
loading,
error,
error
};
};
@@ -120,13 +120,13 @@ const mapDispatchToProps = dispatch => {
return {
fetchRoleByName: (link: string, name: string) => {
dispatch(fetchRoleByName(link, name));
},
}
};
};
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(translate('admin')(SingleRepositoryRole)),
mapDispatchToProps
)(translate("admin")(SingleRepositoryRole))
);

View File

@@ -1,6 +1,6 @@
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_ROLES,
@@ -45,42 +45,42 @@ import reducer, {
deleteRoleSuccess,
getDeleteRoleFailure,
selectListAsCollection,
isPermittedToCreateRoles,
} from './roles';
isPermittedToCreateRoles
} from "./roles";
const role1 = {
name: 'specialrole',
verbs: ['read', 'pull', 'push', 'readPullRequest'],
name: "specialrole",
verbs: ["read", "pull", "push", "readPullRequest"],
system: false,
_links: {
self: {
href: 'http://localhost:8081/scm/api/v2/repositoryRoles/specialrole',
href: "http://localhost:8081/scm/api/v2/repositoryRoles/specialrole"
},
delete: {
href: 'http://localhost:8081/scm/api/v2/repositoryRoles/specialrole',
href: "http://localhost:8081/scm/api/v2/repositoryRoles/specialrole"
},
update: {
href: 'http://localhost:8081/scm/api/v2/repositoryRoles/specialrole',
},
},
href: "http://localhost:8081/scm/api/v2/repositoryRoles/specialrole"
}
}
};
const role2 = {
name: 'WRITE',
name: "WRITE",
verbs: [
'read',
'pull',
'push',
'createPullRequest',
'readPullRequest',
'commentPullRequest',
'mergePullRequest',
"read",
"pull",
"push",
"createPullRequest",
"readPullRequest",
"commentPullRequest",
"mergePullRequest"
],
system: true,
_links: {
self: {
href: 'http://localhost:8081/scm/api/v2/repositoryRoles/WRITE',
},
},
href: "http://localhost:8081/scm/api/v2/repositoryRoles/WRITE"
}
}
};
const responseBody = {
@@ -89,57 +89,57 @@ const responseBody = {
_links: {
self: {
href:
'http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10',
"http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10"
},
first: {
href:
'http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10',
"http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10"
},
last: {
href:
'http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10',
"http://localhost:8081/scm/api/v2/repositoryRoles/?page=0&pageSize=10"
},
create: {
href: 'http://localhost:8081/scm/api/v2/repositoryRoles/',
},
href: "http://localhost:8081/scm/api/v2/repositoryRoles/"
}
},
_embedded: {
repositoryRoles: [role1, role2],
},
repositoryRoles: [role1, role2]
}
};
const response = {
headers: {
'content-type': 'application/json',
"content-type": "application/json"
},
responseBody,
responseBody
};
const URL = 'repositoryRoles';
const ROLES_URL = '/api/v2/repositoryRoles';
const URL = "repositoryRoles";
const ROLES_URL = "/api/v2/repositoryRoles";
const ROLE1_URL =
'http://localhost:8081/scm/api/v2/repositoryRoles/specialrole';
"http://localhost:8081/scm/api/v2/repositoryRoles/specialrole";
const error = new Error('FEHLER!');
const error = new Error("FEHLER!");
describe('repository roles fetch', () => {
describe("repository roles fetch", () => {
const mockStore = configureMockStore([thunk]);
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it('should successfully fetch repository roles', () => {
it("should successfully fetch repository roles", () => {
fetchMock.getOnce(ROLES_URL, response);
const expectedActions = [
{
type: FETCH_ROLES_PENDING,
type: FETCH_ROLES_PENDING
},
{
type: FETCH_ROLES_SUCCESS,
payload: response,
},
payload: response
}
];
const store = mockStore({});
@@ -149,9 +149,9 @@ describe('repository roles fetch', () => {
});
});
it('should fail getting repository roles on HTTP 500', () => {
it("should fail getting repository roles on HTTP 500", () => {
fetchMock.getOnce(ROLES_URL, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -164,11 +164,11 @@ describe('repository roles fetch', () => {
});
});
it('should sucessfully fetch single role by name', () => {
fetchMock.getOnce(ROLES_URL + '/specialrole', role1);
it("should sucessfully fetch single role by name", () => {
fetchMock.getOnce(ROLES_URL + "/specialrole", role1);
const store = mockStore({});
return store.dispatch(fetchRoleByName(URL, 'specialrole')).then(() => {
return store.dispatch(fetchRoleByName(URL, "specialrole")).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_ROLE_PENDING);
expect(actions[1].type).toEqual(FETCH_ROLE_SUCCESS);
@@ -176,13 +176,13 @@ describe('repository roles fetch', () => {
});
});
it('should fail fetching single role by name on HTTP 500', () => {
fetchMock.getOnce(ROLES_URL + '/specialrole', {
status: 500,
it("should fail fetching single role by name on HTTP 500", () => {
fetchMock.getOnce(ROLES_URL + "/specialrole", {
status: 500
});
const store = mockStore({});
return store.dispatch(fetchRoleByName(URL, 'specialrole')).then(() => {
return store.dispatch(fetchRoleByName(URL, "specialrole")).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_ROLE_PENDING);
expect(actions[1].type).toEqual(FETCH_ROLE_FAILURE);
@@ -190,7 +190,7 @@ describe('repository roles fetch', () => {
});
});
it('should sucessfully fetch single role', () => {
it("should sucessfully fetch single role", () => {
fetchMock.getOnce(ROLE1_URL, role1);
const store = mockStore({});
@@ -202,9 +202,9 @@ describe('repository roles fetch', () => {
});
});
it('should fail fetching single role on HTTP 500', () => {
it("should fail fetching single role on HTTP 500", () => {
fetchMock.getOnce(ROLE1_URL, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -216,10 +216,10 @@ describe('repository roles fetch', () => {
});
});
it('should add a role successfully', () => {
it("should add a role successfully", () => {
// unmatched
fetchMock.postOnce(ROLES_URL, {
status: 204,
status: 204
});
// after create, the roles are fetched again
@@ -234,9 +234,9 @@ describe('repository roles fetch', () => {
});
});
it('should fail adding a role on HTTP 500', () => {
it("should fail adding a role on HTTP 500", () => {
fetchMock.postOnce(ROLES_URL, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -249,27 +249,27 @@ describe('repository roles fetch', () => {
});
});
it('should call the callback after role successfully created', () => {
it("should call the callback after role successfully created", () => {
// unmatched
fetchMock.postOnce(ROLES_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(createRole(URL, role1, callback)).then(() => {
expect(callMe).toBe('yeah');
expect(callMe).toBe("yeah");
});
});
it('successfully update role', () => {
it("successfully update role", () => {
fetchMock.putOnce(ROLE1_URL, {
status: 204,
status: 204
});
fetchMock.getOnce(ROLE1_URL, role1);
@@ -283,9 +283,9 @@ describe('repository roles fetch', () => {
});
});
it('should call callback, after successful modified role', () => {
it("should call callback, after successful modified role", () => {
fetchMock.putOnce(ROLE1_URL, {
status: 204,
status: 204
});
fetchMock.getOnce(ROLE1_URL, role1);
@@ -300,9 +300,9 @@ describe('repository roles fetch', () => {
});
});
it('should fail updating role on HTTP 500', () => {
it("should fail updating role on HTTP 500", () => {
fetchMock.putOnce(ROLE1_URL, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -314,9 +314,9 @@ describe('repository roles fetch', () => {
});
});
it('should delete successfully role1', () => {
it("should delete successfully role1", () => {
fetchMock.deleteOnce(ROLE1_URL, {
status: 204,
status: 204
});
const store = mockStore({});
@@ -329,9 +329,9 @@ describe('repository roles fetch', () => {
});
});
it('should call the callback after successful delete', () => {
it("should call the callback after successful delete", () => {
fetchMock.deleteOnce(ROLE1_URL, {
status: 204,
status: 204
});
let called = false;
@@ -345,9 +345,9 @@ describe('repository roles fetch', () => {
});
});
it('should fail to delete role1', () => {
it("should fail to delete role1", () => {
fetchMock.deleteOnce(ROLE1_URL, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -361,325 +361,325 @@ describe('repository roles fetch', () => {
});
});
describe('repository roles reducer', () => {
it('should update state correctly according to FETCH_ROLES_SUCCESS action', () => {
describe("repository roles reducer", () => {
it("should update state correctly according to FETCH_ROLES_SUCCESS action", () => {
const newState = reducer({}, fetchRolesSuccess(responseBody));
expect(newState.list).toEqual({
entries: ['specialrole', 'WRITE'],
entries: ["specialrole", "WRITE"],
entry: {
roleCreatePermission: true,
page: 0,
pageTotal: 1,
_links: responseBody._links,
},
_links: responseBody._links
}
});
expect(newState.byNames).toEqual({
specialrole: role1,
WRITE: role2,
WRITE: role2
});
expect(newState.list.entry.roleCreatePermission).toBeTruthy();
});
it('should set roleCreatePermission to true if update link is present', () => {
it("should set roleCreatePermission to true if update link is present", () => {
const newState = reducer({}, fetchRolesSuccess(responseBody));
expect(newState.list.entry.roleCreatePermission).toBeTruthy();
});
it('should not replace whole byNames map when fetching roles', () => {
it("should not replace whole byNames map when fetching roles", () => {
const oldState = {
byNames: {
WRITE: role2,
},
WRITE: role2
}
};
const newState = reducer(oldState, fetchRolesSuccess(responseBody));
expect(newState.byNames['specialrole']).toBeDefined();
expect(newState.byNames['WRITE']).toBeDefined();
expect(newState.byNames["specialrole"]).toBeDefined();
expect(newState.byNames["WRITE"]).toBeDefined();
});
it('should remove role from state when delete succeeds', () => {
it("should remove role from state when delete succeeds", () => {
const state = {
list: {
entries: ['WRITE', 'specialrole'],
entries: ["WRITE", "specialrole"]
},
byNames: {
specialrole: role1,
WRITE: role2,
},
WRITE: role2
}
};
const newState = reducer(state, deleteRoleSuccess(role2));
expect(newState.byNames['specialrole']).toBeDefined();
expect(newState.byNames['WRITE']).toBeFalsy();
expect(newState.list.entries).toEqual(['specialrole']);
expect(newState.byNames["specialrole"]).toBeDefined();
expect(newState.byNames["WRITE"]).toBeFalsy();
expect(newState.list.entries).toEqual(["specialrole"]);
});
it('should set roleCreatePermission to true if create link is present', () => {
it("should set roleCreatePermission to true if create link is present", () => {
const newState = reducer({}, fetchRolesSuccess(responseBody));
expect(newState.list.entry.roleCreatePermission).toBeTruthy();
expect(newState.list.entries).toEqual(['specialrole', 'WRITE']);
expect(newState.byNames['WRITE']).toBeTruthy();
expect(newState.byNames['specialrole']).toBeTruthy();
expect(newState.list.entries).toEqual(["specialrole", "WRITE"]);
expect(newState.byNames["WRITE"]).toBeTruthy();
expect(newState.byNames["specialrole"]).toBeTruthy();
});
it('should update state according to FETCH_ROLE_SUCCESS action', () => {
it("should update state according to FETCH_ROLE_SUCCESS action", () => {
const newState = reducer({}, fetchRoleSuccess(role2));
expect(newState.byNames['WRITE']).toBe(role2);
expect(newState.byNames["WRITE"]).toBe(role2);
});
it('should affect roles state nor the state of other roles', () => {
it("should affect roles state nor the state of other roles", () => {
const newState = reducer(
{
list: {
entries: ['specialrole'],
},
entries: ["specialrole"]
}
},
fetchRoleSuccess(role2),
fetchRoleSuccess(role2)
);
expect(newState.byNames['WRITE']).toBe(role2);
expect(newState.list.entries).toEqual(['specialrole']);
expect(newState.byNames["WRITE"]).toBe(role2);
expect(newState.list.entries).toEqual(["specialrole"]);
});
});
describe('repository roles selector', () => {
it('should return an empty object', () => {
describe("repository roles selector", () => {
it("should return an empty object", () => {
expect(selectListAsCollection({})).toEqual({});
expect(
selectListAsCollection({
roles: {
a: 'a',
},
}),
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 = {
roles: {
list: {
entry: collection,
},
},
entry: collection
}
}
};
expect(selectListAsCollection(state)).toBe(collection);
});
it('should return false', () => {
it("should return false", () => {
expect(isPermittedToCreateRoles({})).toBe(false);
expect(
isPermittedToCreateRoles({
roles: {
list: {
entry: {},
},
},
}),
entry: {}
}
}
})
).toBe(false);
expect(
isPermittedToCreateRoles({
roles: {
list: {
entry: {
roleCreatePermission: false,
},
},
},
}),
roleCreatePermission: false
}
}
}
})
).toBe(false);
});
it('should return true', () => {
it("should return true", () => {
const state = {
roles: {
list: {
entry: {
roleCreatePermission: true,
},
},
},
roleCreatePermission: true
}
}
}
};
expect(isPermittedToCreateRoles(state)).toBe(true);
});
it('should get repositoryRoles from state', () => {
it("should get repositoryRoles from state", () => {
const state = {
roles: {
list: {
entries: ['a', 'b'],
entries: ["a", "b"]
},
byNames: {
a: {
name: 'a',
name: "a"
},
b: {
name: 'b',
},
},
},
name: "b"
}
}
}
};
expect(getRolesFromState(state)).toEqual([
{
name: 'a',
name: "a"
},
{
name: 'b',
},
name: "b"
}
]);
});
it('should return true, when fetch repositoryRoles is pending', () => {
it("should return true, when fetch repositoryRoles is pending", () => {
const state = {
pending: {
[FETCH_ROLES]: true,
},
[FETCH_ROLES]: true
}
};
expect(isFetchRolesPending(state)).toEqual(true);
});
it('should return false, when fetch repositoryRoles is not pending', () => {
it("should return false, when fetch repositoryRoles is not pending", () => {
expect(isFetchRolesPending({})).toEqual(false);
});
it('should return error when fetch repositoryRoles did fail', () => {
it("should return error when fetch repositoryRoles did fail", () => {
const state = {
failure: {
[FETCH_ROLES]: error,
},
[FETCH_ROLES]: error
}
};
expect(getFetchRolesFailure(state)).toEqual(error);
});
it('should return undefined when fetch repositoryRoles did not fail', () => {
it("should return undefined when fetch repositoryRoles did not fail", () => {
expect(getFetchRolesFailure({})).toBe(undefined);
});
it('should return true if create role is pending', () => {
it("should return true if create role is pending", () => {
const state = {
pending: {
[CREATE_ROLE]: true,
},
[CREATE_ROLE]: true
}
};
expect(isCreateRolePending(state)).toBe(true);
});
it('should return false if create role is not pending', () => {
it("should return false if create role is not pending", () => {
const state = {
pending: {
[CREATE_ROLE]: false,
},
[CREATE_ROLE]: false
}
};
expect(isCreateRolePending(state)).toBe(false);
});
it('should return error when create role did fail', () => {
it("should return error when create role did fail", () => {
const state = {
failure: {
[CREATE_ROLE]: error,
},
[CREATE_ROLE]: error
}
};
expect(getCreateRoleFailure(state)).toEqual(error);
});
it('should return undefined when create role did not fail', () => {
it("should return undefined when create role did not fail", () => {
expect(getCreateRoleFailure({})).toBe(undefined);
});
it('should return role1', () => {
it("should return role1", () => {
const state = {
roles: {
byNames: {
role1: role1,
},
},
role1: role1
}
}
};
expect(getRoleByName(state, 'role1')).toEqual(role1);
expect(getRoleByName(state, "role1")).toEqual(role1);
});
it('should return true, when fetch role2 is pending', () => {
it("should return true, when fetch role2 is pending", () => {
const state = {
pending: {
[FETCH_ROLE + '/role2']: true,
},
[FETCH_ROLE + "/role2"]: true
}
};
expect(isFetchRolePending(state, 'role2')).toEqual(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 false, when fetch role2 is not pending", () => {
expect(isFetchRolePending({}, "role2")).toEqual(false);
});
it('should return error when fetch role2 did fail', () => {
it("should return error when fetch role2 did fail", () => {
const state = {
failure: {
[FETCH_ROLE + '/role2']: error,
},
[FETCH_ROLE + "/role2"]: error
}
};
expect(getFetchRoleFailure(state, 'role2')).toEqual(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 undefined when fetch role2 did not fail", () => {
expect(getFetchRoleFailure({}, "role2")).toBe(undefined);
});
it('should return true, when modify role1 is pending', () => {
it("should return true, when modify role1 is pending", () => {
const state = {
pending: {
[MODIFY_ROLE + '/role1']: true,
},
[MODIFY_ROLE + "/role1"]: true
}
};
expect(isModifyRolePending(state, 'role1')).toEqual(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 false, when modify role1 is not pending", () => {
expect(isModifyRolePending({}, "role1")).toEqual(false);
});
it('should return error when modify role1 did fail', () => {
it("should return error when modify role1 did fail", () => {
const state = {
failure: {
[MODIFY_ROLE + '/role1']: error,
},
[MODIFY_ROLE + "/role1"]: error
}
};
expect(getModifyRoleFailure(state, 'role1')).toEqual(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 undefined when modify role1 did not fail", () => {
expect(getModifyRoleFailure({}, "role1")).toBe(undefined);
});
it('should return true, when delete role2 is pending', () => {
it("should return true, when delete role2 is pending", () => {
const state = {
pending: {
[DELETE_ROLE + '/role2']: true,
},
[DELETE_ROLE + "/role2"]: true
}
};
expect(isDeleteRolePending(state, 'role2')).toEqual(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 false, when delete role2 is not pending", () => {
expect(isDeleteRolePending({}, "role2")).toEqual(false);
});
it('should return error when delete role2 did fail', () => {
it("should return error when delete role2 did fail", () => {
const state = {
failure: {
[DELETE_ROLE + '/role2']: error,
},
[DELETE_ROLE + "/role2"]: error
}
};
expect(getDeleteRoleFailure(state, 'role2')).toEqual(error);
expect(getDeleteRoleFailure(state, "role2")).toEqual(error);
});
it('should return undefined when delete role2 did not fail', () => {
expect(getDeleteRoleFailure({}, 'role2')).toBe(undefined);
it("should return undefined when delete role2 did not fail", () => {
expect(getDeleteRoleFailure({}, "role2")).toBe(undefined);
});
});

View File

@@ -1,55 +1,55 @@
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 { Action, PagedCollection, RepositoryRole } 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 { Action, PagedCollection, RepositoryRole } from "@scm-manager/ui-types";
export const FETCH_ROLES = 'scm/roles/FETCH_ROLES';
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 = "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 = "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 = "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 = "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}`;
export const FETCH_VERBS = 'scm/roles/FETCH_VERBS';
export const FETCH_VERBS = "scm/roles/FETCH_VERBS";
export const FETCH_VERBS_PENDING = `${FETCH_VERBS}_${types.PENDING_SUFFIX}`;
export const FETCH_VERBS_SUCCESS = `${FETCH_VERBS}_${types.SUCCESS_SUFFIX}`;
export const FETCH_VERBS_FAILURE = `${FETCH_VERBS}_${types.FAILURE_SUFFIX}`;
const CONTENT_TYPE_ROLE = 'application/vnd.scmm-repositoryRole+json;v=2';
const CONTENT_TYPE_ROLE = "application/vnd.scmm-repositoryRole+json;v=2";
// fetch roles
export function fetchRolesPending(): Action {
return {
type: FETCH_ROLES_PENDING,
type: FETCH_ROLES_PENDING
};
}
export function fetchRolesSuccess(roles: any): Action {
return {
type: FETCH_ROLES_SUCCESS,
payload: roles,
payload: roles
};
}
@@ -58,8 +58,8 @@ export function fetchRolesFailure(url: string, error: Error): Action {
type: FETCH_ROLES_FAILURE,
payload: {
error,
url,
},
url
}
};
}
@@ -92,7 +92,7 @@ export function fetchRolePending(name: string): Action {
return {
type: FETCH_ROLE_PENDING,
payload: name,
itemId: name,
itemId: name
};
}
@@ -100,7 +100,7 @@ export function fetchRoleSuccess(role: any): Action {
return {
type: FETCH_ROLE_SUCCESS,
payload: role,
itemId: role.name,
itemId: role.name
};
}
@@ -109,9 +109,9 @@ export function fetchRoleFailure(name: string, error: Error): Action {
type: FETCH_ROLE_FAILURE,
payload: {
name,
error,
error
},
itemId: name,
itemId: name
};
}
@@ -133,7 +133,7 @@ function fetchRole(link: string, name: string) {
}
export function fetchRoleByName(link: string, name: string) {
const roleUrl = link.endsWith('/') ? link + name : link + '/' + name;
const roleUrl = link.endsWith("/") ? link + name : link + "/" + name;
return fetchRole(roleUrl, name);
}
@@ -145,33 +145,33 @@ export function fetchRoleByLink(role: RepositoryRole) {
export function createRolePending(role: RepositoryRole): Action {
return {
type: CREATE_ROLE_PENDING,
role,
role
};
}
export function createRoleSuccess(): Action {
return {
type: CREATE_ROLE_SUCCESS,
type: CREATE_ROLE_SUCCESS
};
}
export function createRoleFailure(error: Error): Action {
return {
type: CREATE_ROLE_FAILURE,
payload: error,
payload: error
};
}
export function createRoleReset() {
return {
type: CREATE_ROLE_RESET,
type: CREATE_ROLE_RESET
};
}
export function createRole(
link: string,
role: RepositoryRole,
callback?: () => void,
callback?: () => void
) {
return function(dispatch: Dispatch) {
dispatch(createRolePending(role));
@@ -190,21 +190,21 @@ export function createRole(
//fetch verbs
export function fetchVerbsPending(): Action {
return {
type: FETCH_VERBS_PENDING,
type: FETCH_VERBS_PENDING
};
}
export function fetchVerbsSuccess(verbs: any): Action {
return {
type: FETCH_VERBS_SUCCESS,
payload: verbs,
payload: verbs
};
}
export function fetchVerbsFailure(error: Error): Action {
return {
type: FETCH_VERBS_FAILURE,
payload: error,
payload: error
};
}
@@ -231,7 +231,7 @@ function verbReducer(state: any = {}, action: any = {}) {
const verbs = action.payload.verbs;
return {
...state,
verbs,
verbs
};
default:
return state;
@@ -243,7 +243,7 @@ export function modifyRolePending(role: RepositoryRole): Action {
return {
type: MODIFY_ROLE_PENDING,
payload: role,
itemId: role.name,
itemId: role.name
};
}
@@ -251,7 +251,7 @@ export function modifyRoleSuccess(role: RepositoryRole): Action {
return {
type: MODIFY_ROLE_SUCCESS,
payload: role,
itemId: role.name,
itemId: role.name
};
}
@@ -260,16 +260,16 @@ export function modifyRoleFailure(role: RepositoryRole, error: Error): Action {
type: MODIFY_ROLE_FAILURE,
payload: {
error,
role,
role
},
itemId: role.name,
itemId: role.name
};
}
export function modifyRoleReset(role: RepositoryRole): Action {
return {
type: MODIFY_ROLE_RESET,
itemId: role.name,
itemId: role.name
};
}
@@ -298,7 +298,7 @@ export function deleteRolePending(role: RepositoryRole): Action {
return {
type: DELETE_ROLE_PENDING,
payload: role,
itemId: role.name,
itemId: role.name
};
}
@@ -306,7 +306,7 @@ export function deleteRoleSuccess(role: RepositoryRole): Action {
return {
type: DELETE_ROLE_SUCCESS,
payload: role,
itemId: role.name,
itemId: role.name
};
}
@@ -315,9 +315,9 @@ export function deleteRoleFailure(role: RepositoryRole, error: Error): Action {
type: DELETE_ROLE_FAILURE,
payload: {
error,
role,
role
},
itemId: role.name,
itemId: role.name
};
}
@@ -341,7 +341,7 @@ export function deleteRole(role: RepositoryRole, callback?: () => void) {
function extractRolesByNames(
roles: RepositoryRole[],
roleNames: string[],
oldRolesByNames: object,
oldRolesByNames: object
) {
const rolesByNames = {};
@@ -374,7 +374,7 @@ function deleteRoleInEntries(roles: [], roleName: string) {
const reducerByName = (state: any, rolename: string, newRoleState: any) => {
return {
...state,
[rolename]: newRoleState,
[rolename]: newRoleState
};
};
@@ -390,19 +390,19 @@ function listReducer(state: any = {}, action: any = {}) {
roleCreatePermission: !!action.payload._links.create,
page: action.payload.page,
pageTotal: action.payload.pageTotal,
_links: action.payload._links,
},
_links: action.payload._links
}
};
// Delete single role actions
case DELETE_ROLE_SUCCESS:
const newRoleEntries = deleteRoleInEntries(
state.entries,
action.payload.name,
action.payload.name
);
return {
...state,
entries: newRoleEntries,
entries: newRoleEntries
};
default:
return state;
@@ -417,7 +417,7 @@ function byNamesReducer(state: any = {}, action: any = {}) {
const roleNames = roles.map(role => role.name);
const byNames = extractRolesByNames(roles, roleNames, state.byNames);
return {
...byNames,
...byNames
};
// Fetch single role actions
@@ -435,7 +435,7 @@ function byNamesReducer(state: any = {}, action: any = {}) {
export default combineReducers({
list: listReducer,
byNames: byNamesReducer,
verbs: verbReducer,
verbs: verbReducer
});
// selectors

View File

@@ -1,12 +1,12 @@
import * as React from 'react';
import { translate } from 'react-i18next';
import classNames from 'classnames';
import styled from 'styled-components';
import { InfoItem } from './InfoItem';
import { Icon } from '@scm-manager/ui-components';
import * as React from "react";
import { translate } from "react-i18next";
import classNames from "classnames";
import styled from "styled-components";
import { InfoItem } from "./InfoItem";
import { Icon } from "@scm-manager/ui-components";
type Props = {
type: 'plugin' | 'feature';
type: "plugin" | "feature";
item: InfoItem;
// context props
@@ -40,11 +40,11 @@ const ContentWrapper = styled.div`
class InfoBox extends React.Component<Props> {
renderBody = () => {
const { item, t } = this.props;
const title = item ? item.title : t('login.loading');
const summary = item ? item.summary : t('login.loading');
const title = item ? item.title : t("login.loading");
const summary = item ? item.summary : t("login.loading");
return (
<ContentWrapper className={classNames('media-content', 'content')}>
<ContentWrapper className={classNames("media-content", "content")}>
<h4 className="has-text-link">{title}</h4>
<p>{summary}</p>
</ContentWrapper>
@@ -53,23 +53,23 @@ class InfoBox extends React.Component<Props> {
render() {
const { item, type, t } = this.props;
const icon = type === 'plugin' ? 'puzzle-piece' : 'star';
const icon = type === "plugin" ? "puzzle-piece" : "star";
return (
<BottomMarginA href={item._links.self.href}>
<div className="box media">
<figure className="media-left">
<FixedSizedIconWrapper
className={classNames(
'image',
'box',
'has-text-weight-bold',
'has-text-white',
'has-background-info',
"image",
"box",
"has-text-weight-bold",
"has-text-white",
"has-background-info"
)}
>
<LightBlueIcon className="fa-2x" name={icon} color="inherit" />
<div className="is-size-4">{t('login.' + type)}</div>
<div className="is-size-4">{t('login.tip')}</div>
<div className="is-size-4">{t("login." + type)}</div>
<div className="is-size-4">{t("login.tip")}</div>
</FixedSizedIconWrapper>
</figure>
{this.renderBody()}
@@ -79,4 +79,4 @@ class InfoBox extends React.Component<Props> {
}
}
export default translate('commons')(InfoBox);
export default translate("commons")(InfoBox);

View File

@@ -1,4 +1,4 @@
import { Link } from '@scm-manager/ui-types';
import { Link } from "@scm-manager/ui-types";
export type InfoItem = {
title: string;

View File

@@ -1,13 +1,13 @@
import React from 'react';
import { translate } from 'react-i18next';
import styled from 'styled-components';
import React from "react";
import { translate } from "react-i18next";
import styled from "styled-components";
import {
Image,
ErrorNotification,
InputField,
SubmitButton,
UnauthorizedError,
} from '@scm-manager/ui-components';
UnauthorizedError
} from "@scm-manager/ui-components";
type Props = {
error?: Error;
@@ -45,8 +45,8 @@ class LoginForm extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
username: '',
password: '',
username: "",
password: ""
};
}
@@ -59,13 +59,13 @@ class LoginForm extends React.Component<Props, State> {
handleUsernameChange = (value: string) => {
this.setState({
username: value,
username: value
});
};
handlePasswordChange = (value: string) => {
this.setState({
password: value,
password: value
});
};
@@ -76,7 +76,7 @@ class LoginForm extends React.Component<Props, State> {
areCredentialsInvalid() {
const { t, error } = this.props;
if (error instanceof UnauthorizedError) {
return new Error(t('errorNotification.wrongLoginCredentials'));
return new Error(t("errorNotification.wrongLoginCredentials"));
} else {
return error;
}
@@ -86,26 +86,26 @@ class LoginForm extends React.Component<Props, State> {
const { loading, t } = this.props;
return (
<div className="column is-4 box has-text-centered has-background-white-ter">
<h3 className="title">{t('login.title')}</h3>
<p className="subtitle">{t('login.subtitle')}</p>
<h3 className="title">{t("login.title")}</h3>
<p className="subtitle">{t("login.subtitle")}</p>
<TopMarginBox className="box">
<AvatarWrapper>
<AvatarImage src="/images/blib.jpg" alt={t('login.logo-alt')} />
<AvatarImage src="/images/blib.jpg" alt={t("login.logo-alt")} />
</AvatarWrapper>
<ErrorNotification error={this.areCredentialsInvalid()} />
<form onSubmit={this.handleSubmit}>
<InputField
placeholder={t('login.username-placeholder')}
placeholder={t("login.username-placeholder")}
autofocus={true}
onChange={this.handleUsernameChange}
/>
<InputField
placeholder={t('login.password-placeholder')}
placeholder={t("login.password-placeholder")}
type="password"
onChange={this.handlePasswordChange}
/>
<SubmitButton
label={t('login.submit')}
label={t("login.submit")}
fullWidth={true}
loading={loading}
/>
@@ -116,4 +116,4 @@ class LoginForm extends React.Component<Props, State> {
}
}
export default translate('commons')(LoginForm);
export default translate("commons")(LoginForm);

View File

@@ -1,8 +1,8 @@
import React from 'react';
import InfoBox from './InfoBox';
import { InfoItem } from './InfoItem';
import LoginForm from './LoginForm';
import { Loading } from '@scm-manager/ui-components';
import React from "react";
import InfoBox from "./InfoBox";
import { InfoItem } from "./InfoItem";
import LoginForm from "./LoginForm";
import { Loading } from "@scm-manager/ui-components";
type Props = {
loginInfoLink?: string;
@@ -25,7 +25,7 @@ class LoginInfo extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
loading: !!props.loginInfoLink,
loading: !!props.loginInfoLink
};
}
@@ -35,7 +35,7 @@ class LoginInfo extends React.Component<Props, State> {
.then(info => {
this.setState({
info,
loading: false,
loading: false
});
});
};
@@ -43,7 +43,7 @@ class LoginInfo extends React.Component<Props, State> {
timeout = (ms: number, promise: Promise<any>) => {
return new Promise<LoginInfoResponse>((resolve, reject) => {
setTimeout(() => {
reject(new Error('timeout during fetch of login info'));
reject(new Error("timeout during fetch of login info"));
}, ms);
promise.then(resolve, reject);
});
@@ -56,7 +56,7 @@ class LoginInfo extends React.Component<Props, State> {
}
this.timeout(1000, this.fetchLoginInfo(loginInfoLink)).catch(() => {
this.setState({
loading: false,
loading: false
});
});
}

View File

@@ -1,30 +1,30 @@
import React, { Component } from 'react';
import Main from './Main';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { withRouter } from 'react-router-dom';
import React, { Component } from "react";
import Main from "./Main";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { withRouter } from "react-router-dom";
import {
fetchMe,
getFetchMeFailure,
getMe,
isAuthenticated,
isFetchMePending,
} from '../modules/auth';
isFetchMePending
} from "../modules/auth";
import {
ErrorPage,
Footer,
Header,
Loading,
PrimaryNavigation,
} from '@scm-manager/ui-components';
import { Links, Me } from '@scm-manager/ui-types';
PrimaryNavigation
} from "@scm-manager/ui-components";
import { Links, Me } from "@scm-manager/ui-types";
import {
getFetchIndexResourcesFailure,
getLinks,
getMeLink,
isFetchIndexResourcesPending,
} from '../modules/indexResource';
isFetchIndexResourcesPending
} from "../modules/indexResource";
type Props = {
me: Me;
@@ -52,15 +52,15 @@ class App extends Component<Props> {
const { me, loading, error, authenticated, links, t } = this.props;
let content;
const navigation = authenticated ? <PrimaryNavigation links={links} /> : '';
const navigation = authenticated ? <PrimaryNavigation links={links} /> : "";
if (loading) {
content = <Loading />;
} else if (error) {
content = (
<ErrorPage
title={t('app.error.title')}
subtitle={t('app.error.subtitle')}
title={t("app.error.title")}
subtitle={t("app.error.subtitle")}
error={error}
/>
);
@@ -79,7 +79,7 @@ class App extends Component<Props> {
const mapDispatchToProps = (dispatch: any) => {
return {
fetchMe: (link: string) => dispatch(fetchMe(link)),
fetchMe: (link: string) => dispatch(fetchMe(link))
};
};
@@ -98,13 +98,13 @@ const mapStateToProps = state => {
loading,
error,
links,
meLink,
meLink
};
};
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(translate('commons')(App)),
mapDispatchToProps
)(translate("commons")(App))
);

View File

@@ -1,14 +1,14 @@
import React from 'react';
import React from "react";
import {
ErrorNotification,
InputField,
Notification,
PasswordConfirmation,
SubmitButton,
} from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
import { Me } from '@scm-manager/ui-types';
import { changePassword } from '../modules/changePassword';
SubmitButton
} from "@scm-manager/ui-components";
import { translate } from "react-i18next";
import { Me } from "@scm-manager/ui-types";
import { changePassword } from "../modules/changePassword";
type Props = {
me: Me;
@@ -29,21 +29,21 @@ class ChangeUserPassword extends React.Component<Props, State> {
super(props);
this.state = {
oldPassword: '',
password: '',
oldPassword: "",
password: "",
loading: false,
passwordConfirmationError: false,
validatePasswordError: false,
validatePassword: '',
validatePassword: "",
passwordChanged: false,
passwordValid: false,
passwordValid: false
};
}
setLoadingState = () => {
this.setState({
...this.state,
loading: true,
loading: true
});
};
@@ -51,7 +51,7 @@ class ChangeUserPassword extends React.Component<Props, State> {
this.setState({
...this.state,
error: error,
loading: false,
loading: false
});
};
@@ -60,8 +60,8 @@ class ChangeUserPassword extends React.Component<Props, State> {
...this.state,
loading: false,
passwordChanged: true,
oldPassword: '',
password: '',
oldPassword: "",
password: ""
});
};
@@ -97,8 +97,8 @@ class ChangeUserPassword extends React.Component<Props, State> {
if (passwordChanged) {
message = (
<Notification
type={'success'}
children={t('password.changedSuccessfully')}
type={"success"}
children={t("password.changedSuccessfully")}
onClose={() => this.onClose()}
/>
);
@@ -112,29 +112,29 @@ class ChangeUserPassword extends React.Component<Props, State> {
<div className="columns">
<div className="column">
<InputField
label={t('password.currentPassword')}
label={t("password.currentPassword")}
type="password"
onChange={oldPassword =>
this.setState({
...this.state,
oldPassword,
oldPassword
})
}
value={this.state.oldPassword ? this.state.oldPassword : ''}
helpText={t('password.currentPasswordHelpText')}
value={this.state.oldPassword ? this.state.oldPassword : ""}
helpText={t("password.currentPasswordHelpText")}
/>
</div>
</div>
<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.isValid()}
loading={loading}
label={t('password.submit')}
label={t("password.submit")}
/>
</div>
</div>
@@ -146,16 +146,16 @@ class ChangeUserPassword 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('commons')(ChangeUserPassword);
export default translate("commons")(ChangeUserPassword);

View File

@@ -1,20 +1,20 @@
import React, { Component } from 'react';
import App from './App';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { withRouter } from 'react-router-dom';
import React, { Component } from "react";
import App from "./App";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { withRouter } from "react-router-dom";
import { Loading, ErrorBoundary } from '@scm-manager/ui-components';
import { Loading, ErrorBoundary } from "@scm-manager/ui-components";
import {
fetchIndexResources,
getFetchIndexResourcesFailure,
getLinks,
isFetchIndexResourcesPending,
} from '../modules/indexResource';
import PluginLoader from './PluginLoader';
import { IndexResources } from '@scm-manager/ui-types';
import ScrollToTop from './ScrollToTop';
import IndexErrorPage from './IndexErrorPage';
isFetchIndexResourcesPending
} from "../modules/indexResource";
import PluginLoader from "./PluginLoader";
import { IndexResources } from "@scm-manager/ui-types";
import ScrollToTop from "./ScrollToTop";
import IndexErrorPage from "./IndexErrorPage";
type Props = {
error: Error;
@@ -36,7 +36,7 @@ class Index extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
pluginsLoaded: false,
pluginsLoaded: false
};
}
@@ -46,7 +46,7 @@ class Index extends Component<Props, State> {
pluginLoaderCallback = () => {
this.setState({
pluginsLoaded: true,
pluginsLoaded: true
});
};
@@ -77,7 +77,7 @@ class Index extends Component<Props, State> {
const mapDispatchToProps = (dispatch: any) => {
return {
fetchIndexResources: () => dispatch(fetchIndexResources()),
fetchIndexResources: () => dispatch(fetchIndexResources())
};
};
@@ -88,13 +88,13 @@ const mapStateToProps = state => {
return {
loading,
error,
indexResources,
indexResources
};
};
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(translate('commons')(Index)),
mapDispatchToProps
)(translate("commons")(Index))
);

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { translate, TFunction } from 'react-i18next';
import { ErrorPage } from '@scm-manager/ui-components';
import React from "react";
import { translate, TFunction } from "react-i18next";
import { ErrorPage } from "@scm-manager/ui-components";
type Props = {
error: Error;
@@ -12,12 +12,12 @@ class IndexErrorPage extends React.Component<Props> {
const { error, t } = this.props;
return (
<ErrorPage
title={t('app.error.title')}
subtitle={t('app.error.subtitle')}
title={t("app.error.title")}
subtitle={t("app.error.subtitle")}
error={error}
/>
);
}
}
export default translate('commons')(IndexErrorPage);
export default translate("commons")(IndexErrorPage);

View File

@@ -1,17 +1,17 @@
import React from 'react';
import { connect } from 'react-redux';
import { Redirect, withRouter } from 'react-router-dom';
import { compose } from 'redux';
import { translate } from 'react-i18next';
import styled from 'styled-components';
import React from "react";
import { connect } from "react-redux";
import { Redirect, withRouter } from "react-router-dom";
import { compose } from "redux";
import { translate } from "react-i18next";
import styled from "styled-components";
import {
getLoginFailure,
isAuthenticated,
isLoginPending,
login,
} from '../modules/auth';
import { getLoginInfoLink, getLoginLink } from '../modules/indexResource';
import LoginInfo from '../components/LoginInfo';
login
} from "../modules/auth";
import { getLoginInfoLink, getLoginLink } from "../modules/indexResource";
import LoginInfo from "../components/LoginInfo";
type Props = {
authenticated: boolean;
@@ -42,8 +42,8 @@ class Login extends React.Component<Props> {
renderRedirect = () => {
const { from } = this.props.location.state || {
from: {
pathname: '/',
},
pathname: "/"
}
};
return <Redirect to={from} />;
};
@@ -80,14 +80,14 @@ const mapStateToProps = state => {
loading,
error,
link,
loginInfoLink,
loginInfoLink
};
};
const mapDispatchToProps = dispatch => {
return {
login: (loginLink: string, username: string, password: string) =>
dispatch(login(loginLink, username, password)),
dispatch(login(loginLink, username, password))
};
};
@@ -95,6 +95,6 @@ export default compose(
withRouter,
connect(
mapStateToProps,
mapDispatchToProps,
),
mapDispatchToProps
)
)(Login);

View File

@@ -1,17 +1,17 @@
import React from 'react';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { Redirect } from 'react-router-dom';
import React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { Redirect } from "react-router-dom";
import {
logout,
isAuthenticated,
isLogoutPending,
getLogoutFailure,
isRedirecting,
} from '../modules/auth';
import { Loading, ErrorPage } from '@scm-manager/ui-components';
import { getLogoutLink } from '../modules/indexResource';
isRedirecting
} from "../modules/auth";
import { Loading, ErrorPage } from "@scm-manager/ui-components";
import { getLogoutLink } from "../modules/indexResource";
type Props = {
authenticated: boolean;
@@ -37,8 +37,8 @@ class Logout extends React.Component<Props> {
if (error) {
return (
<ErrorPage
title={t('logout.error.title')}
subtitle={t('logout.error.subtitle')}
title={t("logout.error.title")}
subtitle={t("logout.error.subtitle")}
error={error}
/>
);
@@ -61,17 +61,17 @@ const mapStateToProps = state => {
loading,
redirecting,
error,
logoutLink,
logoutLink
};
};
const mapDispatchToProps = dispatch => {
return {
logout: (link: string) => dispatch(logout(link)),
logout: (link: string) => dispatch(logout(link))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(translate('commons')(Logout));
mapDispatchToProps
)(translate("commons")(Logout));

View File

@@ -1,28 +1,28 @@
import React from 'react';
import React from "react";
import { Redirect, Route, Switch, withRouter } from 'react-router-dom';
import { Links } from '@scm-manager/ui-types';
import { Redirect, Route, Switch, withRouter } from "react-router-dom";
import { Links } from "@scm-manager/ui-types";
import Overview from '../repos/containers/Overview';
import Users from '../users/containers/Users';
import Login from '../containers/Login';
import Logout from '../containers/Logout';
import Overview from "../repos/containers/Overview";
import Users from "../users/containers/Users";
import Login from "../containers/Login";
import Logout from "../containers/Logout";
import { ProtectedRoute } from '@scm-manager/ui-components';
import { binder, ExtensionPoint } from '@scm-manager/ui-extensions';
import { ProtectedRoute } from "@scm-manager/ui-components";
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
import CreateUser from '../users/containers/CreateUser';
import SingleUser from '../users/containers/SingleUser';
import RepositoryRoot from '../repos/containers/RepositoryRoot';
import Create from '../repos/containers/Create';
import CreateUser from "../users/containers/CreateUser";
import SingleUser from "../users/containers/SingleUser";
import RepositoryRoot from "../repos/containers/RepositoryRoot";
import Create from "../repos/containers/Create";
import Groups from '../groups/containers/Groups';
import SingleGroup from '../groups/containers/SingleGroup';
import CreateGroup from '../groups/containers/CreateGroup';
import Groups from "../groups/containers/Groups";
import SingleGroup from "../groups/containers/SingleGroup";
import CreateGroup from "../groups/containers/CreateGroup";
import Admin from '../admin/containers/Admin';
import Admin from "../admin/containers/Admin";
import Profile from './Profile';
import Profile from "./Profile";
type Props = {
authenticated?: boolean;
@@ -32,8 +32,8 @@ type Props = {
class Main extends React.Component<Props> {
render() {
const { authenticated, links } = this.props;
const redirectUrlFactory = binder.getExtension('main.redirect', this.props);
let url = '/repos/';
const redirectUrlFactory = binder.getExtension("main.redirect", this.props);
let url = "/repos/";
if (redirectUrlFactory) {
url = redirectUrlFactory(this.props);
}
@@ -128,7 +128,7 @@ class Main extends React.Component<Props> {
renderAll={true}
props={{
authenticated,
links,
links
}}
/>
</Switch>

View File

@@ -1,8 +1,8 @@
import * as React from 'react';
import { apiClient, Loading } from '@scm-manager/ui-components';
import { getUiPluginsLink } from '../modules/indexResource';
import { connect } from 'react-redux';
import loadBundle from './loadBundle';
import * as React from "react";
import { apiClient, Loading } from "@scm-manager/ui-components";
import { getUiPluginsLink } from "../modules/indexResource";
import { connect } from "react-redux";
import loadBundle from "./loadBundle";
type Props = {
loaded: boolean;
@@ -24,7 +24,7 @@ class PluginLoader extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
message: 'booting',
message: "booting"
};
}
@@ -32,7 +32,7 @@ class PluginLoader extends React.Component<Props, State> {
const { loaded } = this.props;
if (!loaded) {
this.setState({
message: 'loading plugin information',
message: "loading plugin information"
});
this.getPlugins(this.props.link);
@@ -51,7 +51,7 @@ class PluginLoader extends React.Component<Props, State> {
loadPlugins = (plugins: Plugin[]) => {
this.setState({
message: 'loading plugins',
message: "loading plugins"
});
const promises = [];
@@ -68,7 +68,7 @@ class PluginLoader extends React.Component<Props, State> {
loadPlugin = (plugin: Plugin) => {
this.setState({
message: `loading ${plugin.name}`,
message: `loading ${plugin.name}`
});
const promises = [];
@@ -99,7 +99,7 @@ const comparePluginsByName = (a: Plugin, b: Plugin) => {
const mapStateToProps = state => {
const link = getUiPluginsLink(state);
return {
link,
link
};
};

View File

@@ -1,22 +1,22 @@
import React from 'react';
import React from "react";
import { Route, withRouter } from 'react-router-dom';
import { getMe } from '../modules/auth';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { Me } from '@scm-manager/ui-types';
import { Route, withRouter } from "react-router-dom";
import { getMe } from "../modules/auth";
import { compose } from "redux";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { Me } from "@scm-manager/ui-types";
import {
ErrorPage,
Page,
Navigation,
SubNavigation,
Section,
NavLink,
} from '@scm-manager/ui-components';
import ChangeUserPassword from './ChangeUserPassword';
import ProfileInfo from './ProfileInfo';
import { ExtensionPoint } from '@scm-manager/ui-extensions';
NavLink
} from "@scm-manager/ui-components";
import ChangeUserPassword from "./ChangeUserPassword";
import ProfileInfo from "./ProfileInfo";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
type Props = {
me: Me;
@@ -29,7 +29,7 @@ type State = {};
class Profile extends React.Component<Props, State> {
stripEndingSlash = (url: string) => {
if (url.endsWith('/')) {
if (url.endsWith("/")) {
return url.substring(0, url.length - 2);
}
return url;
@@ -47,11 +47,11 @@ class Profile extends React.Component<Props, State> {
if (!me) {
return (
<ErrorPage
title={t('profile.error-title')}
subtitle={t('profile.error-subtitle')}
title={t("profile.error-title")}
subtitle={t("profile.error-subtitle")}
error={{
name: t('profile.error'),
message: t('profile.error-message'),
name: t("profile.error"),
message: t("profile.error-message")
}}
/>
);
@@ -59,7 +59,7 @@ class Profile extends React.Component<Props, State> {
const extensionProps = {
me,
url,
url
};
return (
@@ -79,19 +79,19 @@ class Profile extends React.Component<Props, State> {
</div>
<div className="column">
<Navigation>
<Section label={t('profile.navigationLabel')}>
<Section label={t("profile.navigationLabel")}>
<NavLink
to={`${url}`}
icon="fas fa-info-circle"
label={t('profile.informationNavLink')}
label={t("profile.informationNavLink")}
/>
<SubNavigation
to={`${url}/settings/password`}
label={t('profile.settingsNavLink')}
label={t("profile.settingsNavLink")}
>
<NavLink
to={`${url}/settings/password`}
label={t('profile.changePasswordNavLink')}
label={t("profile.changePasswordNavLink")}
/>
<ExtensionPoint
name="profile.setting"
@@ -110,12 +110,12 @@ class Profile extends React.Component<Props, State> {
const mapStateToProps = state => {
return {
me: getMe(state),
me: getMe(state)
};
};
export default compose(
translate('commons'),
translate("commons"),
connect(mapStateToProps),
withRouter,
withRouter
)(Profile);

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Me } from '@scm-manager/ui-types';
import React from "react";
import { translate } from "react-i18next";
import { Me } from "@scm-manager/ui-types";
import {
MailLink,
AvatarWrapper,
AvatarImage,
} from '@scm-manager/ui-components';
AvatarImage
} from "@scm-manager/ui-components";
type Props = {
me: Me;
@@ -30,15 +30,15 @@ class ProfileInfo extends React.Component<Props> {
<table className="table content">
<tbody>
<tr>
<th>{t('profile.username')}</th>
<th>{t("profile.username")}</th>
<td>{me.name}</td>
</tr>
<tr>
<th>{t('profile.displayName')}</th>
<th>{t("profile.displayName")}</th>
<td>{me.displayName}</td>
</tr>
<tr>
<th>{t('profile.mail')}</th>
<th>{t("profile.mail")}</th>
<td>
<MailLink address={me.mail} />
</td>
@@ -58,7 +58,7 @@ class ProfileInfo extends React.Component<Props> {
if (me.groups.length > 0) {
groups = (
<tr>
<th>{t('profile.groups')}</th>
<th>{t("profile.groups")}</th>
<td className="is-paddingless">
<ul>
{me.groups.map(group => {
@@ -73,4 +73,4 @@ class ProfileInfo extends React.Component<Props> {
}
}
export default translate('commons')(ProfileInfo);
export default translate("commons")(ProfileInfo);

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { withRouter } from 'react-router-dom';
import React from "react";
import { withRouter } from "react-router-dom";
type Props = {
location: any;

View File

@@ -1,33 +1,33 @@
/* global SystemJS */
// eslint-disable-next-line import/no-webpack-loader-syntax
import 'script-loader!../../../../node_modules/systemjs/dist/system.js';
import "script-loader!../../../../node_modules/systemjs/dist/system.js";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as ReactRouterDom from 'react-router-dom';
import * as Redux from 'redux';
import * as ReactRedux from 'react-redux';
import SytleComponentsDefault from 'styled-components';
import * as SytleComponents from 'styled-components';
import * as ReactI18Next from 'react-i18next';
import ClassNamesDefault from 'classnames';
import * as ClassNames from 'classnames';
import QueryStringDefault from 'query-string';
import * as QueryString from 'query-string';
import * as UIExtensions from '@scm-manager/ui-extensions';
import * as UIComponents from '@scm-manager/ui-components';
import { urls } from '@scm-manager/ui-components';
import * as React from "react";
import * as ReactDOM from "react-dom";
import * as ReactRouterDom from "react-router-dom";
import * as Redux from "redux";
import * as ReactRedux from "react-redux";
import SytleComponentsDefault from "styled-components";
import * as SytleComponents from "styled-components";
import * as ReactI18Next from "react-i18next";
import ClassNamesDefault from "classnames";
import * as ClassNames from "classnames";
import QueryStringDefault from "query-string";
import * as QueryString from "query-string";
import * as UIExtensions from "@scm-manager/ui-extensions";
import * as UIComponents from "@scm-manager/ui-components";
import { urls } from "@scm-manager/ui-components";
// TODO add headers "Cache": "no-cache", "X-Requested-With": "XMLHttpRequest"
SystemJS.config({
baseURL: urls.withContextPath('/assets'),
baseURL: urls.withContextPath("/assets"),
meta: {
'/*': {
"/*": {
esModule: true,
authorization: true,
},
},
authorization: true
}
}
});
const expose = (name, cmp, defaultCmp) => {
@@ -37,22 +37,22 @@ const expose = (name, cmp, defaultCmp) => {
// https://github.com/systemjs/systemjs/issues/1749
mod = {
...cmp,
__useDefault: defaultCmp,
__useDefault: defaultCmp
};
}
SystemJS.set(name, SystemJS.newModule(mod));
};
expose('react', React);
expose('react-dom', ReactDOM);
expose('react-router-dom', ReactRouterDom);
expose('styled-components', SytleComponents, SytleComponentsDefault);
expose('redux', Redux);
expose('react-redux', ReactRedux);
expose('react-i18next', ReactI18Next);
expose('classnames', ClassNames, ClassNamesDefault);
expose('query-string', QueryString, QueryStringDefault);
expose('@scm-manager/ui-extensions', UIExtensions);
expose('@scm-manager/ui-components', UIComponents);
expose("react", React);
expose("react-dom", ReactDOM);
expose("react-router-dom", ReactRouterDom);
expose("styled-components", SytleComponents, SytleComponentsDefault);
expose("redux", Redux);
expose("react-redux", ReactRedux);
expose("react-i18next", ReactI18Next);
expose("classnames", ClassNames, ClassNamesDefault);
expose("query-string", QueryString, QueryStringDefault);
expose("@scm-manager/ui-extensions", UIExtensions);
expose("@scm-manager/ui-components", UIComponents);
export default plugin => SystemJS.import(plugin);

View File

@@ -1,23 +1,23 @@
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
import users from './users/modules/users';
import repos from './repos/modules/repos';
import repositoryTypes from './repos/modules/repositoryTypes';
import changesets from './repos/modules/changesets';
import sources from './repos/sources/modules/sources';
import groups from './groups/modules/groups';
import auth from './modules/auth';
import pending from './modules/pending';
import failure from './modules/failure';
import permissions from './repos/permissions/modules/permissions';
import config from './admin/modules/config';
import roles from './admin/roles/modules/roles';
import namespaceStrategies from './admin/modules/namespaceStrategies';
import indexResources from './modules/indexResource';
import plugins from './admin/plugins/modules/plugins';
import thunk from "redux-thunk";
import logger from "redux-logger";
import { createStore, compose, applyMiddleware, combineReducers } from "redux";
import users from "./users/modules/users";
import repos from "./repos/modules/repos";
import repositoryTypes from "./repos/modules/repositoryTypes";
import changesets from "./repos/modules/changesets";
import sources from "./repos/sources/modules/sources";
import groups from "./groups/modules/groups";
import auth from "./modules/auth";
import pending from "./modules/pending";
import failure from "./modules/failure";
import permissions from "./repos/permissions/modules/permissions";
import config from "./admin/modules/config";
import roles from "./admin/roles/modules/roles";
import namespaceStrategies from "./admin/modules/namespaceStrategies";
import indexResources from "./modules/indexResource";
import plugins from "./admin/plugins/modules/plugins";
import branches from './repos/branches/modules/branches';
import branches from "./repos/branches/modules/branches";
function createReduxStore() {
const composeEnhancers =
@@ -39,7 +39,7 @@ function createReduxStore() {
roles,
sources,
namespaceStrategies,
plugins,
plugins
});
return createStore(reducer, composeEnhancers(applyMiddleware(thunk, logger)));

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { translate } from 'react-i18next';
import React from "react";
import { translate } from "react-i18next";
import {
Subtitle,
AutocompleteAddEntryToTableField,
@@ -7,11 +7,11 @@ import {
InputField,
SubmitButton,
Textarea,
Checkbox,
} from '@scm-manager/ui-components';
import { Group, SelectValue } from '@scm-manager/ui-types';
Checkbox
} from "@scm-manager/ui-components";
import { Group, SelectValue } from "@scm-manager/ui-types";
import * as validator from './groupValidation';
import * as validator from "./groupValidation";
type Props = {
t: (p: string) => string;
@@ -31,17 +31,17 @@ class GroupForm extends React.Component<Props, State> {
super(props);
this.state = {
group: {
name: '',
description: '',
name: "",
description: "",
_embedded: {
members: [],
members: []
},
_links: {},
members: [],
type: '',
external: false,
type: "",
external: false
},
nameValidationError: false,
nameValidationError: false
};
}
@@ -51,8 +51,8 @@ class GroupForm extends React.Component<Props, State> {
this.setState({
...this.state,
group: {
...group,
},
...group
}
});
}
}
@@ -92,12 +92,12 @@ class GroupForm extends React.Component<Props, State> {
<AutocompleteAddEntryToTableField
addEntry={this.addMember}
disabled={false}
buttonLabel={t('add-member-button.label')}
errorMessage={t('add-member-textfield.error')}
buttonLabel={t("add-member-button.label")}
errorMessage={t("add-member-textfield.error")}
loadSuggestions={loadUserSuggestions}
placeholder={t('add-member-autocomplete.placeholder')}
loadingMessage={t('add-member-autocomplete.loading')}
noOptionsMessage={t('add-member-autocomplete.no-options')}
placeholder={t("add-member-autocomplete.placeholder")}
loadingMessage={t("add-member-autocomplete.loading")}
noOptionsMessage={t("add-member-autocomplete.no-options")}
/>
</>
);
@@ -110,9 +110,9 @@ class GroupForm extends React.Component<Props, State> {
}
return (
<Checkbox
label={t('group.external')}
label={t("group.external")}
checked={group.external}
helpText={t('groupForm.help.externalHelpText')}
helpText={t("groupForm.help.externalHelpText")}
onChange={this.handleExternalChange}
/>
);
@@ -129,18 +129,18 @@ class GroupForm extends React.Component<Props, State> {
// create new group
nameField = (
<InputField
label={t('group.name')}
errorMessage={t('groupForm.nameError')}
label={t("group.name")}
errorMessage={t("groupForm.nameError")}
onChange={this.handleGroupNameChange}
value={group.name}
validationError={this.state.nameValidationError}
helpText={t('groupForm.help.nameHelpText')}
helpText={t("groupForm.help.nameHelpText")}
/>
);
} else if (group.external) {
subtitle = <Subtitle subtitle={t('groupForm.externalSubtitle')} />;
subtitle = <Subtitle subtitle={t("groupForm.externalSubtitle")} />;
} else {
subtitle = <Subtitle subtitle={t('groupForm.subtitle')} />;
subtitle = <Subtitle subtitle={t("groupForm.subtitle")} />;
}
return (
@@ -149,18 +149,18 @@ class GroupForm extends React.Component<Props, State> {
<form onSubmit={this.submit}>
{nameField}
<Textarea
label={t('group.description')}
errorMessage={t('groupForm.descriptionError')}
label={t("group.description")}
errorMessage={t("groupForm.descriptionError")}
onChange={this.handleDescriptionChange}
value={group.description}
validationError={false}
helpText={t('groupForm.help.descriptionHelpText')}
helpText={t("groupForm.help.descriptionHelpText")}
/>
{this.renderExternalField(group)}
{this.renderMemberfields(group)}
<SubmitButton
disabled={!this.isValid()}
label={t('groupForm.submit')}
label={t("groupForm.submit")}
loading={loading}
/>
</form>
@@ -173,8 +173,8 @@ class GroupForm extends React.Component<Props, State> {
...this.state,
group: {
...this.state.group,
members: membernames,
},
members: membernames
}
});
};
@@ -187,8 +187,8 @@ class GroupForm extends React.Component<Props, State> {
...this.state,
group: {
...this.state.group,
members: [...this.state.group.members, value.value.id],
},
members: [...this.state.group.members, value.value.id]
}
});
};
@@ -201,8 +201,8 @@ class GroupForm extends React.Component<Props, State> {
nameValidationError: !validator.isNameValid(name),
group: {
...this.state.group,
name,
},
name
}
});
};
@@ -210,8 +210,8 @@ class GroupForm extends React.Component<Props, State> {
this.setState({
group: {
...this.state.group,
description,
},
description
}
});
};
@@ -219,10 +219,10 @@ class GroupForm extends React.Component<Props, State> {
this.setState({
group: {
...this.state.group,
external,
},
external
}
});
};
}
export default translate('groups')(GroupForm);
export default translate("groups")(GroupForm);

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { Group } from '@scm-manager/ui-types';
import { NavLink } from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
import React from "react";
import { Group } from "@scm-manager/ui-types";
import { NavLink } from "@scm-manager/ui-components";
import { translate } from "react-i18next";
type Props = {
group: Group;
@@ -21,9 +21,9 @@ class EditGroupNavLink extends React.Component<Props> {
return null;
}
return (
<NavLink to={editUrl} label={t('singleGroup.menu.generalNavLink')} />
<NavLink to={editUrl} label={t("singleGroup.menu.generalNavLink")} />
);
}
}
export default translate('groups')(EditGroupNavLink);
export default translate("groups")(EditGroupNavLink);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Group } from '@scm-manager/ui-types';
import { NavLink } from '@scm-manager/ui-components';
import React from "react";
import { translate } from "react-i18next";
import { Group } from "@scm-manager/ui-types";
import { NavLink } from "@scm-manager/ui-components";
type Props = {
t: (p: string) => string;
@@ -19,7 +19,7 @@ class ChangePermissionNavLink extends React.Component<Props> {
return (
<NavLink
to={permissionsUrl}
label={t('singleGroup.menu.setPermissionsNavLink')}
label={t("singleGroup.menu.setPermissionsNavLink")}
/>
);
}
@@ -29,4 +29,4 @@ class ChangePermissionNavLink extends React.Component<Props> {
};
}
export default translate('groups')(ChangePermissionNavLink);
export default translate("groups")(ChangePermissionNavLink);

View File

@@ -1,2 +1,2 @@
export { default as EditGroupNavLink } from './EditGroupNavLink';
export { default as SetPermissionsNavLink } from './SetPermissionsNavLink';
export { default as EditGroupNavLink } from "./EditGroupNavLink";
export { default as SetPermissionsNavLink } from "./SetPermissionsNavLink";

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Group } from '@scm-manager/ui-types';
import { DateFromNow, Checkbox } from '@scm-manager/ui-components';
import GroupMember from './GroupMember';
import React from "react";
import { translate } from "react-i18next";
import { Group } from "@scm-manager/ui-types";
import { DateFromNow, Checkbox } from "@scm-manager/ui-components";
import GroupMember from "./GroupMember";
type Props = {
group: Group;
@@ -18,31 +18,31 @@ class Details extends React.Component<Props> {
<table className="table content">
<tbody>
<tr>
<th>{t('group.name')}</th>
<th>{t("group.name")}</th>
<td>{group.name}</td>
</tr>
<tr>
<th>{t('group.description')}</th>
<th>{t("group.description")}</th>
<td>{group.description}</td>
</tr>
<tr>
<th>{t('group.external')}</th>
<th>{t("group.external")}</th>
<td>
<Checkbox checked={group.external} />
</td>
</tr>
<tr>
<th>{t('group.type')}</th>
<th>{t("group.type")}</th>
<td>{group.type}</td>
</tr>
<tr>
<th>{t('group.creationDate')}</th>
<th>{t("group.creationDate")}</th>
<td>
<DateFromNow date={group.creationDate} />
</td>
</tr>
<tr>
<th>{t('group.lastModified')}</th>
<th>{t("group.lastModified")}</th>
<td>
<DateFromNow date={group.lastModified} />
</td>
@@ -60,7 +60,7 @@ class Details extends React.Component<Props> {
if (group.members.length > 0) {
member = (
<tr>
<th>{t('group.members')}</th>
<th>{t("group.members")}</th>
<td className="is-paddingless">
<ul>
{group._embedded.members.map((member, index) => {
@@ -75,4 +75,4 @@ class Details extends React.Component<Props> {
}
}
export default translate('groups')(Details);
export default translate("groups")(Details);

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Member } from '@scm-manager/ui-types';
import React from "react";
import { Link } from "react-router-dom";
import { Member } from "@scm-manager/ui-types";
type Props = {
member: Member;

View File

@@ -1,8 +1,8 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Link } from 'react-router-dom';
import { Group } from '@scm-manager/ui-types';
import { Icon } from '@scm-manager/ui-components';
import React from "react";
import { translate } from "react-i18next";
import { Link } from "react-router-dom";
import { Group } from "@scm-manager/ui-types";
import { Icon } from "@scm-manager/ui-components";
type Props = {
group: Group;
@@ -20,9 +20,9 @@ class GroupRow extends React.Component<Props> {
const { group, t } = this.props;
const to = `/group/${group.name}`;
const iconType = group.external ? (
<Icon title={t('group.external')} name="sign-out-alt fa-rotate-270" />
<Icon title={t("group.external")} name="sign-out-alt fa-rotate-270" />
) : (
<Icon title={t('group.internal')} name="sign-in-alt fa-rotate-90" />
<Icon title={t("group.internal")} name="sign-in-alt fa-rotate-90" />
);
return (
@@ -36,4 +36,4 @@ class GroupRow extends React.Component<Props> {
}
}
export default translate('groups')(GroupRow);
export default translate("groups")(GroupRow);

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { translate } from 'react-i18next';
import GroupRow from './GroupRow';
import { Group } from '@scm-manager/ui-types';
import React from "react";
import { translate } from "react-i18next";
import GroupRow from "./GroupRow";
import { Group } from "@scm-manager/ui-types";
type Props = {
t: (p: string) => string;
@@ -15,8 +15,8 @@ class GroupTable extends React.Component<Props> {
<table className="card-table table is-hoverable is-fullwidth">
<thead>
<tr>
<th>{t('group.name')}</th>
<th className="is-hidden-mobile">{t('group.description')}</th>
<th>{t("group.name")}</th>
<th className="is-hidden-mobile">{t("group.description")}</th>
</tr>
</thead>
<tbody>
@@ -29,4 +29,4 @@ class GroupTable extends React.Component<Props> {
}
}
export default translate('groups')(GroupTable);
export default translate("groups")(GroupTable);

View File

@@ -1,3 +1,3 @@
export { default as Details } from './Details';
export { default as GroupRow } from './GroupRow';
export { default as GroupTable } from './GroupTable';
export { default as Details } from "./Details";
export { default as GroupRow } from "./GroupRow";
export { default as GroupTable } from "./GroupTable";

View File

@@ -1,21 +1,21 @@
import React from 'react';
import React from "react";
import { Page } from '@scm-manager/ui-components';
import { translate } from 'react-i18next';
import GroupForm from '../components/GroupForm';
import { connect } from 'react-redux';
import { Page } from "@scm-manager/ui-components";
import { translate } from "react-i18next";
import GroupForm from "../components/GroupForm";
import { connect } from "react-redux";
import {
createGroup,
isCreateGroupPending,
getCreateGroupFailure,
createGroupReset,
} from '../modules/groups';
import { Group } from '@scm-manager/ui-types';
import { History } from 'history';
createGroupReset
} from "../modules/groups";
import { Group } from "@scm-manager/ui-types";
import { History } from "history";
import {
getGroupsLink,
getUserAutoCompleteLink,
} from '../../modules/indexResource';
getUserAutoCompleteLink
} from "../../modules/indexResource";
type Props = {
t: (p: string) => string;
@@ -39,8 +39,8 @@ class CreateGroup extends React.Component<Props, State> {
const { t, loading, error } = this.props;
return (
<Page
title={t('add-group.title')}
subtitle={t('add-group.subtitle')}
title={t("add-group.title")}
subtitle={t("add-group.subtitle")}
error={error}
>
<div>
@@ -55,24 +55,24 @@ class CreateGroup extends React.Component<Props, State> {
}
loadUserAutocompletion = (inputValue: string) => {
const url = this.props.autocompleteLink + '?q=';
const url = this.props.autocompleteLink + "?q=";
return fetch(url + inputValue)
.then(response => response.json())
.then(json => {
return json.map(element => {
return {
value: element,
label: `${element.displayName} (${element.id})`,
label: `${element.displayName} (${element.id})`
};
});
});
};
groupCreated = (group: Group) => {
this.props.history.push('/group/' + group.name);
this.props.history.push("/group/" + group.name);
};
createGroup = (group: Group) => {
this.props.createGroup(this.props.createLink, group, () =>
this.groupCreated(group),
this.groupCreated(group)
);
};
}
@@ -83,7 +83,7 @@ const mapDispatchToProps = dispatch => {
dispatch(createGroup(link, group, callback)),
resetForm: () => {
dispatch(createGroupReset());
},
}
};
};
@@ -96,11 +96,11 @@ const mapStateToProps = state => {
createLink,
loading,
error,
autocompleteLink,
autocompleteLink
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(translate('groups')(CreateGroup));
mapDispatchToProps
)(translate("groups")(CreateGroup));

View File

@@ -1,20 +1,20 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Group } from '@scm-manager/ui-types';
import React from "react";
import { translate } from "react-i18next";
import { Group } from "@scm-manager/ui-types";
import {
Subtitle,
DeleteButton,
confirmAlert,
ErrorNotification,
} from '@scm-manager/ui-components';
ErrorNotification
} from "@scm-manager/ui-components";
import {
deleteGroup,
getDeleteGroupFailure,
isDeleteGroupPending,
} from '../modules/groups';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { History } from 'history';
isDeleteGroupPending
} from "../modules/groups";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import { History } from "history";
type Props = {
loading: boolean;
@@ -30,7 +30,7 @@ type Props = {
export class DeleteGroup extends React.Component<Props> {
static defaultProps = {
confirmDialog: true,
confirmDialog: true
};
deleteGroup = () => {
@@ -38,24 +38,24 @@ export class DeleteGroup extends React.Component<Props> {
};
groupDeleted = () => {
this.props.history.push('/groups/');
this.props.history.push("/groups/");
};
confirmDelete = () => {
const { t } = this.props;
confirmAlert({
title: t('deleteGroup.confirmAlert.title'),
message: t('deleteGroup.confirmAlert.message'),
title: t("deleteGroup.confirmAlert.title"),
message: t("deleteGroup.confirmAlert.message"),
buttons: [
{
label: t('deleteGroup.confirmAlert.submit'),
onClick: () => this.deleteGroup(),
label: t("deleteGroup.confirmAlert.submit"),
onClick: () => this.deleteGroup()
},
{
label: t('deleteGroup.confirmAlert.cancel'),
onClick: () => null,
},
],
label: t("deleteGroup.confirmAlert.cancel"),
onClick: () => null
}
]
});
};
@@ -73,12 +73,12 @@ export class DeleteGroup extends React.Component<Props> {
return (
<>
<Subtitle subtitle={t('deleteGroup.subtitle')} />
<Subtitle subtitle={t("deleteGroup.subtitle")} />
<ErrorNotification error={error} />
<div className="columns">
<div className="column">
<DeleteButton
label={t('deleteGroup.button')}
label={t("deleteGroup.button")}
action={action}
loading={loading}
/>
@@ -94,7 +94,7 @@ const mapStateToProps = (state, ownProps) => {
const error = getDeleteGroupFailure(state, ownProps.group.name);
return {
loading,
error,
error
};
};
@@ -102,11 +102,11 @@ const mapDispatchToProps = dispatch => {
return {
deleteGroup: (group: Group, callback?: () => void) => {
dispatch(deleteGroup(group, callback));
},
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(withRouter(translate('groups')(DeleteGroup)));
mapDispatchToProps
)(withRouter(translate("groups")(DeleteGroup)));

View File

@@ -1,18 +1,18 @@
import React from 'react';
import { connect } from 'react-redux';
import GroupForm from '../components/GroupForm';
import React from "react";
import { connect } from "react-redux";
import GroupForm from "../components/GroupForm";
import {
modifyGroup,
getModifyGroupFailure,
isModifyGroupPending,
modifyGroupReset,
} from '../modules/groups';
import { History } from 'history';
import { withRouter } from 'react-router-dom';
import { Group } from '@scm-manager/ui-types';
import { ErrorNotification } from '@scm-manager/ui-components';
import { getUserAutoCompleteLink } from '../../modules/indexResource';
import DeleteGroup from './DeleteGroup';
modifyGroupReset
} from "../modules/groups";
import { History } from "history";
import { withRouter } from "react-router-dom";
import { Group } from "@scm-manager/ui-types";
import { ErrorNotification } from "@scm-manager/ui-components";
import { getUserAutoCompleteLink } from "../../modules/indexResource";
import DeleteGroup from "./DeleteGroup";
type Props = {
group: Group;
@@ -40,14 +40,14 @@ class EditGroup extends React.Component<Props> {
};
loadUserAutocompletion = (inputValue: string) => {
const url = this.props.autocompleteLink + '?q=';
const url = this.props.autocompleteLink + "?q=";
return fetch(url + inputValue)
.then(response => response.json())
.then(json => {
return json.map(element => {
return {
value: element,
label: `${element.displayName} (${element.id})`,
label: `${element.displayName} (${element.id})`
};
});
});
@@ -80,7 +80,7 @@ const mapStateToProps = (state, ownProps) => {
return {
loading,
error,
autocompleteLink,
autocompleteLink
};
};
@@ -91,11 +91,11 @@ const mapDispatchToProps = dispatch => {
},
modifyGroupReset: (group: Group) => {
dispatch(modifyGroupReset(group));
},
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
mapDispatchToProps
)(withRouter(EditGroup));

View File

@@ -1,16 +1,16 @@
import React from 'react';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import { History } from 'history';
import { Group, 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 { Group, PagedCollection } from "@scm-manager/ui-types";
import {
fetchGroupsByPage,
getGroupsFromState,
isFetchGroupsPending,
getFetchGroupsFailure,
isPermittedToCreateGroups,
selectListAsCollection,
} from '../modules/groups';
selectListAsCollection
} from "../modules/groups";
import {
Page,
PageActions,
@@ -18,10 +18,10 @@ import {
Notification,
LinkPaginator,
urls,
CreateButton,
} from '@scm-manager/ui-components';
import { GroupTable } from './../components/table';
import { getGroupsLink } from '../../modules/indexResource';
CreateButton
} from "@scm-manager/ui-components";
import { GroupTable } from "./../components/table";
import { getGroupsLink } from "../../modules/indexResource";
type Props = {
groups: Group[];
@@ -47,7 +47,7 @@ class Groups extends React.Component<Props> {
fetchGroupsByPage(
groupLink,
page,
urls.getQueryStringFromLocation(location),
urls.getQueryStringFromLocation(location)
);
}
@@ -58,7 +58,7 @@ class Groups extends React.Component<Props> {
page,
groupLink,
location,
fetchGroupsByPage,
fetchGroupsByPage
} = this.props;
if (list && page && !loading) {
const statePage: number = list.page + 1;
@@ -66,7 +66,7 @@ class Groups extends React.Component<Props> {
fetchGroupsByPage(
groupLink,
page,
urls.getQueryStringFromLocation(location),
urls.getQueryStringFromLocation(location)
);
}
}
@@ -76,8 +76,8 @@ class Groups extends React.Component<Props> {
const { groups, loading, error, canAddGroups, t } = this.props;
return (
<Page
title={t('groups.title')}
subtitle={t('groups.subtitle')}
title={t("groups.title")}
subtitle={t("groups.subtitle")}
loading={loading || !groups}
error={error}
>
@@ -87,7 +87,7 @@ class Groups extends React.Component<Props> {
<OverviewPageActions
showCreateButton={canAddGroups}
link="groups"
label={t('create-group-button.label')}
label={t("create-group-button.label")}
/>
</PageActions>
</Page>
@@ -108,7 +108,7 @@ class Groups extends React.Component<Props> {
</>
);
}
return <Notification type="info">{t('groups.noGroups')}</Notification>;
return <Notification type="info">{t("groups.noGroups")}</Notification>;
}
renderCreateButton() {
@@ -116,7 +116,7 @@ class Groups extends React.Component<Props> {
if (canAddGroups) {
return (
<CreateButton
label={t('create-group-button.label')}
label={t("create-group-button.label")}
link="/groups/create"
/>
);
@@ -142,7 +142,7 @@ const mapStateToProps = (state, ownProps) => {
canAddGroups,
list,
page,
groupLink,
groupLink
};
};
@@ -150,11 +150,11 @@ const mapDispatchToProps = dispatch => {
return {
fetchGroupsByPage: (link: string, page: number, filter?: string) => {
dispatch(fetchGroupsByPage(link, page, filter));
},
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(translate('groups')(Groups));
mapDispatchToProps
)(translate("groups")(Groups));

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import React from "react";
import { connect } from "react-redux";
import {
Page,
ErrorPage,
@@ -7,28 +7,28 @@ import {
Navigation,
SubNavigation,
Section,
NavLink,
} from '@scm-manager/ui-components';
import { Route } from 'react-router-dom';
import { Details } from './../components/table';
NavLink
} from "@scm-manager/ui-components";
import { Route } from "react-router-dom";
import { Details } from "./../components/table";
import {
EditGroupNavLink,
SetPermissionsNavLink,
} from './../components/navLinks';
import { Group } from '@scm-manager/ui-types';
import { History } from 'history';
SetPermissionsNavLink
} from "./../components/navLinks";
import { Group } from "@scm-manager/ui-types";
import { History } from "history";
import {
fetchGroupByName,
getGroupByName,
isFetchGroupPending,
getFetchGroupFailure,
} from '../modules/groups';
getFetchGroupFailure
} from "../modules/groups";
import { translate } from 'react-i18next';
import EditGroup from './EditGroup';
import { getGroupsLink } from '../../modules/indexResource';
import SetPermissions from '../../permissions/components/SetPermissions';
import { ExtensionPoint } from '@scm-manager/ui-extensions';
import { translate } from "react-i18next";
import EditGroup from "./EditGroup";
import { getGroupsLink } from "../../modules/indexResource";
import SetPermissions from "../../permissions/components/SetPermissions";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
type Props = {
name: string;
@@ -52,7 +52,7 @@ class SingleGroup extends React.Component<Props> {
}
stripEndingSlash = (url: string) => {
if (url.endsWith('/')) {
if (url.endsWith("/")) {
return url.substring(0, url.length - 2);
}
return url;
@@ -68,8 +68,8 @@ class SingleGroup extends React.Component<Props> {
if (error) {
return (
<ErrorPage
title={t('singleGroup.errorTitle')}
subtitle={t('singleGroup.errorSubtitle')}
title={t("singleGroup.errorTitle")}
subtitle={t("singleGroup.errorSubtitle")}
error={error}
/>
);
@@ -83,7 +83,7 @@ class SingleGroup extends React.Component<Props> {
const extensionProps = {
group,
url,
url
};
return (
@@ -117,11 +117,11 @@ class SingleGroup extends React.Component<Props> {
</div>
<div className="column">
<Navigation>
<Section label={t('singleGroup.menu.navigationLabel')}>
<Section label={t("singleGroup.menu.navigationLabel")}>
<NavLink
to={`${url}`}
icon="fas fa-info-circle"
label={t('singleGroup.menu.informationNavLink')}
label={t("singleGroup.menu.informationNavLink")}
/>
<ExtensionPoint
name="group.navigation"
@@ -130,7 +130,7 @@ class SingleGroup extends React.Component<Props> {
/>
<SubNavigation
to={`${url}/settings/general`}
label={t('singleGroup.menu.settingsNavLink')}
label={t("singleGroup.menu.settingsNavLink")}
>
<EditGroupNavLink
group={group}
@@ -167,7 +167,7 @@ const mapStateToProps = (state, ownProps) => {
group,
loading,
error,
groupLink,
groupLink
};
};
@@ -175,11 +175,11 @@ const mapDispatchToProps = dispatch => {
return {
fetchGroupByName: (link: string, name: string) => {
dispatch(fetchGroupByName(link, name));
},
}
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(translate('groups')(SingleGroup));
mapDispatchToProps
)(translate("groups")(SingleGroup));

View File

@@ -1,6 +1,6 @@
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, {
fetchGroups,
@@ -43,67 +43,67 @@ import reducer, {
MODIFY_GROUP_PENDING,
MODIFY_GROUP_SUCCESS,
MODIFY_GROUP_FAILURE,
getCreateGroupLink,
} from './groups';
const GROUPS_URL = '/api/v2/groups';
const URL_HUMAN_GROUP = 'http://localhost:8081/api/v2/groups/humanGroup';
const URL = '/groups';
getCreateGroupLink
} from "./groups";
const GROUPS_URL = "/api/v2/groups";
const URL_HUMAN_GROUP = "http://localhost:8081/api/v2/groups/humanGroup";
const URL = "/groups";
const error = new Error('You have an error!');
const error = new Error("You have an error!");
const humanGroup = {
creationDate: '2018-07-31T08:39:07.860Z',
description: 'This is a group',
name: 'humanGroup',
type: 'xml',
creationDate: "2018-07-31T08:39:07.860Z",
description: "This is a group",
name: "humanGroup",
type: "xml",
properties: {},
members: ['userZaphod'],
members: ["userZaphod"],
_links: {
self: {
href: URL_HUMAN_GROUP,
href: URL_HUMAN_GROUP
},
delete: {
href: URL_HUMAN_GROUP,
href: URL_HUMAN_GROUP
},
update: {
href: URL_HUMAN_GROUP,
},
href: URL_HUMAN_GROUP
}
},
_embedded: {
members: [
{
name: 'userZaphod',
name: "userZaphod",
_links: {
self: {
href: 'http://localhost:8081/api/v2/users/userZaphod',
},
},
},
],
},
href: "http://localhost:8081/api/v2/users/userZaphod"
}
}
}
]
}
};
const emptyGroup = {
creationDate: '2018-07-31T08:39:07.860Z',
description: 'This is a group',
name: 'emptyGroup',
type: 'xml',
creationDate: "2018-07-31T08:39:07.860Z",
description: "This is a group",
name: "emptyGroup",
type: "xml",
properties: {},
members: [],
_links: {
self: {
href: 'http://localhost:8081/api/v2/groups/emptyGroup',
href: "http://localhost:8081/api/v2/groups/emptyGroup"
},
delete: {
href: 'http://localhost:8081/api/v2/groups/emptyGroup',
href: "http://localhost:8081/api/v2/groups/emptyGroup"
},
update: {
href: 'http://localhost:8081/api/v2/groups/emptyGroup',
},
href: "http://localhost:8081/api/v2/groups/emptyGroup"
}
},
_embedded: {
members: [],
},
members: []
}
};
const responseBody = {
@@ -111,48 +111,48 @@ const responseBody = {
pageTotal: 1,
_links: {
self: {
href: 'http://localhost:3000/api/v2/groups/?page=0&pageSize=10',
href: "http://localhost:3000/api/v2/groups/?page=0&pageSize=10"
},
first: {
href: 'http://localhost:3000/api/v2/groups/?page=0&pageSize=10',
href: "http://localhost:3000/api/v2/groups/?page=0&pageSize=10"
},
last: {
href: 'http://localhost:3000/api/v2/groups/?page=0&pageSize=10',
href: "http://localhost:3000/api/v2/groups/?page=0&pageSize=10"
},
create: {
href: 'http://localhost:3000/api/v2/groups/',
},
href: "http://localhost:3000/api/v2/groups/"
}
},
_embedded: {
groups: [humanGroup, emptyGroup],
},
groups: [humanGroup, emptyGroup]
}
};
const response = {
headers: {
'content-type': 'application/json',
"content-type": "application/json"
},
responseBody,
responseBody
};
describe('groups fetch()', () => {
describe("groups fetch()", () => {
const mockStore = configureMockStore([thunk]);
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it('should successfully fetch groups', () => {
it("should successfully fetch groups", () => {
fetchMock.getOnce(GROUPS_URL, response);
const expectedActions = [
{
type: FETCH_GROUPS_PENDING,
type: FETCH_GROUPS_PENDING
},
{
type: FETCH_GROUPS_SUCCESS,
payload: response,
},
payload: response
}
];
const store = mockStore({});
@@ -162,9 +162,9 @@ describe('groups fetch()', () => {
});
});
it('should fail getting groups on HTTP 500', () => {
it("should fail getting groups on HTTP 500", () => {
fetchMock.getOnce(GROUPS_URL, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -176,11 +176,11 @@ describe('groups fetch()', () => {
});
});
it('should sucessfully fetch single group by name', () => {
fetchMock.getOnce(GROUPS_URL + '/humanGroup', humanGroup);
it("should sucessfully fetch single group by name", () => {
fetchMock.getOnce(GROUPS_URL + "/humanGroup", humanGroup);
const store = mockStore({});
return store.dispatch(fetchGroupByName(URL, 'humanGroup')).then(() => {
return store.dispatch(fetchGroupByName(URL, "humanGroup")).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_GROUP_PENDING);
expect(actions[1].type).toEqual(FETCH_GROUP_SUCCESS);
@@ -188,13 +188,13 @@ describe('groups fetch()', () => {
});
});
it('should fail fetching single group by name on HTTP 500', () => {
fetchMock.getOnce(GROUPS_URL + '/humanGroup', {
status: 500,
it("should fail fetching single group by name on HTTP 500", () => {
fetchMock.getOnce(GROUPS_URL + "/humanGroup", {
status: 500
});
const store = mockStore({});
return store.dispatch(fetchGroupByName(URL, 'humanGroup')).then(() => {
return store.dispatch(fetchGroupByName(URL, "humanGroup")).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_GROUP_PENDING);
expect(actions[1].type).toEqual(FETCH_GROUP_FAILURE);
@@ -202,7 +202,7 @@ describe('groups fetch()', () => {
});
});
it('should sucessfully fetch single group', () => {
it("should sucessfully fetch single group", () => {
fetchMock.getOnce(URL_HUMAN_GROUP, humanGroup);
const store = mockStore({});
@@ -214,9 +214,9 @@ describe('groups fetch()', () => {
});
});
it('should fail fetching single group on HTTP 500', () => {
it("should fail fetching single group on HTTP 500", () => {
fetchMock.getOnce(URL_HUMAN_GROUP, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -228,9 +228,9 @@ describe('groups fetch()', () => {
});
});
it('should successfully create group', () => {
it("should successfully create group", () => {
fetchMock.postOnce(GROUPS_URL, {
status: 201,
status: 201
});
const store = mockStore({});
@@ -241,9 +241,9 @@ describe('groups fetch()', () => {
});
});
it('should call the callback after creating group', () => {
it("should call the callback after creating group", () => {
fetchMock.postOnce(GROUPS_URL, {
status: 201,
status: 201
});
let called = false;
@@ -259,9 +259,9 @@ describe('groups fetch()', () => {
});
});
it('should fail creating group on HTTP 500', () => {
it("should fail creating group on HTTP 500", () => {
fetchMock.postOnce(GROUPS_URL, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -274,9 +274,9 @@ describe('groups fetch()', () => {
});
});
it('should successfully modify group', () => {
it("should successfully modify group", () => {
fetchMock.putOnce(URL_HUMAN_GROUP, {
status: 204,
status: 204
});
fetchMock.getOnce(URL_HUMAN_GROUP, humanGroup);
@@ -291,9 +291,9 @@ describe('groups fetch()', () => {
});
});
it('should call the callback after modifying group', () => {
it("should call the callback after modifying group", () => {
fetchMock.putOnce(URL_HUMAN_GROUP, {
status: 204,
status: 204
});
fetchMock.getOnce(URL_HUMAN_GROUP, humanGroup);
@@ -312,9 +312,9 @@ describe('groups fetch()', () => {
});
});
it('should fail modifying group on HTTP 500', () => {
it("should fail modifying group on HTTP 500", () => {
fetchMock.putOnce(URL_HUMAN_GROUP, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -327,9 +327,9 @@ describe('groups fetch()', () => {
});
});
it('should delete successfully group humanGroup', () => {
it("should delete successfully group humanGroup", () => {
fetchMock.deleteOnce(URL_HUMAN_GROUP, {
status: 204,
status: 204
});
const store = mockStore({});
@@ -342,9 +342,9 @@ describe('groups fetch()', () => {
});
});
it('should call the callback, after successful delete', () => {
it("should call the callback, after successful delete", () => {
fetchMock.deleteOnce(URL_HUMAN_GROUP, {
status: 204,
status: 204
});
let called = false;
@@ -358,9 +358,9 @@ describe('groups fetch()', () => {
});
});
it('should fail to delete group humanGroup', () => {
it("should fail to delete group humanGroup", () => {
fetchMock.deleteOnce(URL_HUMAN_GROUP, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -374,339 +374,339 @@ describe('groups fetch()', () => {
});
});
describe('groups reducer', () => {
it('should update state correctly according to FETCH_GROUPS_SUCCESS action', () => {
describe("groups reducer", () => {
it("should update state correctly according to FETCH_GROUPS_SUCCESS action", () => {
const newState = reducer({}, fetchGroupsSuccess(responseBody));
expect(newState.list).toEqual({
entries: ['humanGroup', 'emptyGroup'],
entries: ["humanGroup", "emptyGroup"],
entry: {
groupCreatePermission: true,
page: 0,
pageTotal: 1,
_links: responseBody._links,
},
_links: responseBody._links
}
});
expect(newState.byNames).toEqual({
humanGroup: humanGroup,
emptyGroup: emptyGroup,
emptyGroup: emptyGroup
});
expect(newState.list.entry.groupCreatePermission).toBeTruthy();
});
it('should set groupCreatePermission to true if update link is present', () => {
it("should set groupCreatePermission to true if update link is present", () => {
const newState = reducer({}, fetchGroupsSuccess(responseBody));
expect(newState.list.entry.groupCreatePermission).toBeTruthy();
});
it('should not replace whole byNames map when fetching groups', () => {
it("should not replace whole byNames map when fetching groups", () => {
const oldState = {
byNames: {
emptyGroup: emptyGroup,
},
emptyGroup: emptyGroup
}
};
const newState = reducer(oldState, fetchGroupsSuccess(responseBody));
expect(newState.byNames['humanGroup']).toBeDefined();
expect(newState.byNames['emptyGroup']).toBeDefined();
expect(newState.byNames["humanGroup"]).toBeDefined();
expect(newState.byNames["emptyGroup"]).toBeDefined();
});
it('should set groupCreatePermission to true if create link is present', () => {
it("should set groupCreatePermission to true if create link is present", () => {
const newState = reducer({}, fetchGroupsSuccess(responseBody));
expect(newState.list.entry.groupCreatePermission).toBeTruthy();
expect(newState.list.entries).toEqual(['humanGroup', 'emptyGroup']);
expect(newState.byNames['emptyGroup']).toBeTruthy();
expect(newState.byNames['humanGroup']).toBeTruthy();
expect(newState.list.entries).toEqual(["humanGroup", "emptyGroup"]);
expect(newState.byNames["emptyGroup"]).toBeTruthy();
expect(newState.byNames["humanGroup"]).toBeTruthy();
});
it('should update state according to FETCH_GROUP_SUCCESS action', () => {
it("should update state according to FETCH_GROUP_SUCCESS action", () => {
const newState = reducer({}, fetchGroupSuccess(emptyGroup));
expect(newState.byNames['emptyGroup']).toBe(emptyGroup);
expect(newState.byNames["emptyGroup"]).toBe(emptyGroup);
});
it('should affect groups state nor the state of other groups', () => {
it("should affect groups state nor the state of other groups", () => {
const newState = reducer(
{
list: {
entries: ['humanGroup'],
},
entries: ["humanGroup"]
}
},
fetchGroupSuccess(emptyGroup),
fetchGroupSuccess(emptyGroup)
);
expect(newState.byNames['emptyGroup']).toBe(emptyGroup);
expect(newState.list.entries).toEqual(['humanGroup']);
expect(newState.byNames["emptyGroup"]).toBe(emptyGroup);
expect(newState.list.entries).toEqual(["humanGroup"]);
});
it('should remove group from state when delete succeeds', () => {
it("should remove group from state when delete succeeds", () => {
const state = {
list: {
entries: ['humanGroup', 'emptyGroup'],
entries: ["humanGroup", "emptyGroup"]
},
byNames: {
humanGroup: humanGroup,
emptyGroup: emptyGroup,
},
emptyGroup: emptyGroup
}
};
const newState = reducer(state, deleteGroupSuccess(emptyGroup));
expect(newState.byNames['humanGroup']).toBeDefined();
expect(newState.byNames['emptyGroup']).toBeFalsy();
expect(newState.list.entries).toEqual(['humanGroup']);
expect(newState.byNames["humanGroup"]).toBeDefined();
expect(newState.byNames["emptyGroup"]).toBeFalsy();
expect(newState.list.entries).toEqual(["humanGroup"]);
});
});
describe('selector tests', () => {
it('should return an empty object', () => {
describe("selector tests", () => {
it("should return an empty object", () => {
expect(selectListAsCollection({})).toEqual({});
expect(
selectListAsCollection({
groups: {
a: 'a',
},
}),
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 = {
groups: {
list: {
entry: collection,
},
},
entry: collection
}
}
};
expect(selectListAsCollection(state)).toBe(collection);
});
it('should return false when groupCreatePermission is false', () => {
it("should return false when groupCreatePermission is false", () => {
expect(isPermittedToCreateGroups({})).toBe(false);
expect(
isPermittedToCreateGroups({
groups: {
list: {
entry: {},
},
},
}),
entry: {}
}
}
})
).toBe(false);
expect(
isPermittedToCreateGroups({
groups: {
list: {
entry: {
groupCreatePermission: false,
},
},
},
}),
groupCreatePermission: false
}
}
}
})
).toBe(false);
});
it('should return true when groupCreatePermission is true', () => {
it("should return true when groupCreatePermission is true", () => {
const state = {
groups: {
list: {
entry: {
groupCreatePermission: true,
},
},
},
groupCreatePermission: true
}
}
}
};
expect(isPermittedToCreateGroups(state)).toBe(true);
});
it('should return create Group link', () => {
it("should return create Group link", () => {
const state = {
groups: {
list: {
entry: {
_links: {
create: {
href: '/create',
},
},
},
},
},
href: "/create"
}
}
}
}
}
};
expect(getCreateGroupLink(state)).toBe('/create');
expect(getCreateGroupLink(state)).toBe("/create");
});
it('should get groups from state', () => {
it("should get groups from state", () => {
const state = {
groups: {
list: {
entries: ['a', 'b'],
entries: ["a", "b"]
},
byNames: {
a: {
name: 'a',
name: "a"
},
b: {
name: 'b',
},
},
},
name: "b"
}
}
}
};
expect(getGroupsFromState(state)).toEqual([
{
name: 'a',
name: "a"
},
{
name: 'b',
},
name: "b"
}
]);
});
it('should return null when there are no groups in the state', () => {
it("should return null when there are no groups in the state", () => {
expect(getGroupsFromState({})).toBe(null);
});
it('should return true, when fetch groups is pending', () => {
it("should return true, when fetch groups is pending", () => {
const state = {
pending: {
[FETCH_GROUPS]: true,
},
[FETCH_GROUPS]: true
}
};
expect(isFetchGroupsPending(state)).toEqual(true);
});
it('should return false, when fetch groups is not pending', () => {
it("should return false, when fetch groups is not pending", () => {
expect(isFetchGroupsPending({})).toEqual(false);
});
it('should return error when fetch groups did fail', () => {
it("should return error when fetch groups did fail", () => {
const state = {
failure: {
[FETCH_GROUPS]: error,
},
[FETCH_GROUPS]: error
}
};
expect(getFetchGroupsFailure(state)).toEqual(error);
});
it('should return undefined when fetch groups did not fail', () => {
it("should return undefined when fetch groups did not fail", () => {
expect(getFetchGroupsFailure({})).toBe(undefined);
});
it('should return group emptyGroup', () => {
it("should return group emptyGroup", () => {
const state = {
groups: {
byNames: {
emptyGroup: emptyGroup,
},
},
emptyGroup: emptyGroup
}
}
};
expect(getGroupByName(state, 'emptyGroup')).toEqual(emptyGroup);
expect(getGroupByName(state, "emptyGroup")).toEqual(emptyGroup);
});
it('should return true, when fetch group humanGroup is pending', () => {
it("should return true, when fetch group humanGroup is pending", () => {
const state = {
pending: {
[FETCH_GROUP + '/humanGroup']: true,
},
[FETCH_GROUP + "/humanGroup"]: true
}
};
expect(isFetchGroupPending(state, 'humanGroup')).toEqual(true);
expect(isFetchGroupPending(state, "humanGroup")).toEqual(true);
});
it('should return false, when fetch group humanGroup is not pending', () => {
expect(isFetchGroupPending({}, 'humanGroup')).toEqual(false);
it("should return false, when fetch group humanGroup is not pending", () => {
expect(isFetchGroupPending({}, "humanGroup")).toEqual(false);
});
it('should return error when fetch group humanGroup did fail', () => {
it("should return error when fetch group humanGroup did fail", () => {
const state = {
failure: {
[FETCH_GROUP + '/humanGroup']: error,
},
[FETCH_GROUP + "/humanGroup"]: error
}
};
expect(getFetchGroupFailure(state, 'humanGroup')).toEqual(error);
expect(getFetchGroupFailure(state, "humanGroup")).toEqual(error);
});
it('should return undefined when fetch group humanGroup did not fail', () => {
expect(getFetchGroupFailure({}, 'humanGroup')).toBe(undefined);
it("should return undefined when fetch group humanGroup did not fail", () => {
expect(getFetchGroupFailure({}, "humanGroup")).toBe(undefined);
});
it('should return true if create group is pending', () => {
it("should return true if create group is pending", () => {
expect(
isCreateGroupPending({
pending: {
[CREATE_GROUP]: true,
},
}),
[CREATE_GROUP]: true
}
})
).toBeTruthy();
});
it('should return false if create group is not pending', () => {
it("should return false if create group is not pending", () => {
expect(isCreateGroupPending({})).toBe(false);
});
it('should return error if creating group failed', () => {
it("should return error if creating group failed", () => {
expect(
getCreateGroupFailure({
failure: {
[CREATE_GROUP]: error,
},
}),
[CREATE_GROUP]: error
}
})
).toEqual(error);
});
it('should return undefined if creating group did not fail', () => {
it("should return undefined if creating group did not fail", () => {
expect(getCreateGroupFailure({})).toBeUndefined();
});
it('should return true, when delete group humanGroup is pending', () => {
it("should return true, when delete group humanGroup is pending", () => {
const state = {
pending: {
[DELETE_GROUP + '/humanGroup']: true,
},
[DELETE_GROUP + "/humanGroup"]: true
}
};
expect(isDeleteGroupPending(state, 'humanGroup')).toEqual(true);
expect(isDeleteGroupPending(state, "humanGroup")).toEqual(true);
});
it('should return false, when delete group humanGroup is not pending', () => {
expect(isDeleteGroupPending({}, 'humanGroup')).toEqual(false);
it("should return false, when delete group humanGroup is not pending", () => {
expect(isDeleteGroupPending({}, "humanGroup")).toEqual(false);
});
it('should return error when delete group humanGroup did fail', () => {
it("should return error when delete group humanGroup did fail", () => {
const state = {
failure: {
[DELETE_GROUP + '/humanGroup']: error,
},
[DELETE_GROUP + "/humanGroup"]: error
}
};
expect(getDeleteGroupFailure(state, 'humanGroup')).toEqual(error);
expect(getDeleteGroupFailure(state, "humanGroup")).toEqual(error);
});
it('should return undefined when delete group humanGroup did not fail', () => {
expect(getDeleteGroupFailure({}, 'humanGroup')).toBe(undefined);
it("should return undefined when delete group humanGroup did not fail", () => {
expect(getDeleteGroupFailure({}, "humanGroup")).toBe(undefined);
});
it('should return true, if createGroup is pending', () => {
it("should return true, if createGroup is pending", () => {
const state = {
pending: {
[CREATE_GROUP]: true,
},
[CREATE_GROUP]: true
}
};
expect(isCreateGroupPending(state)).toBe(true);
});
it('should return false, if createGroup is not pending', () => {
it("should return false, if createGroup is not pending", () => {
expect(isCreateGroupPending({})).toBe(false);
});
it('should return error of createGroup failed', () => {
it("should return error of createGroup failed", () => {
const state = {
failure: {
[CREATE_GROUP]: error,
},
[CREATE_GROUP]: error
}
};
expect(getCreateGroupFailure(state)).toEqual(error);
});

View File

@@ -1,38 +1,38 @@
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 { Action, PagedCollection, Group } 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 { Action, PagedCollection, Group } from "@scm-manager/ui-types";
export const FETCH_GROUPS = 'scm/groups/FETCH_GROUPS';
export const FETCH_GROUPS = "scm/groups/FETCH_GROUPS";
export const FETCH_GROUPS_PENDING = `${FETCH_GROUPS}_${types.PENDING_SUFFIX}`;
export const FETCH_GROUPS_SUCCESS = `${FETCH_GROUPS}_${types.SUCCESS_SUFFIX}`;
export const FETCH_GROUPS_FAILURE = `${FETCH_GROUPS}_${types.FAILURE_SUFFIX}`;
export const FETCH_GROUP = 'scm/groups/FETCH_GROUP';
export const FETCH_GROUP = "scm/groups/FETCH_GROUP";
export const FETCH_GROUP_PENDING = `${FETCH_GROUP}_${types.PENDING_SUFFIX}`;
export const FETCH_GROUP_SUCCESS = `${FETCH_GROUP}_${types.SUCCESS_SUFFIX}`;
export const FETCH_GROUP_FAILURE = `${FETCH_GROUP}_${types.FAILURE_SUFFIX}`;
export const CREATE_GROUP = 'scm/groups/CREATE_GROUP';
export const CREATE_GROUP = "scm/groups/CREATE_GROUP";
export const CREATE_GROUP_PENDING = `${CREATE_GROUP}_${types.PENDING_SUFFIX}`;
export const CREATE_GROUP_SUCCESS = `${CREATE_GROUP}_${types.SUCCESS_SUFFIX}`;
export const CREATE_GROUP_FAILURE = `${CREATE_GROUP}_${types.FAILURE_SUFFIX}`;
export const CREATE_GROUP_RESET = `${CREATE_GROUP}_${types.RESET_SUFFIX}`;
export const MODIFY_GROUP = 'scm/groups/MODIFY_GROUP';
export const MODIFY_GROUP = "scm/groups/MODIFY_GROUP";
export const MODIFY_GROUP_PENDING = `${MODIFY_GROUP}_${types.PENDING_SUFFIX}`;
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_GROUP';
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}`;
const CONTENT_TYPE_GROUP = 'application/vnd.scmm-group+json;v=2';
const CONTENT_TYPE_GROUP = "application/vnd.scmm-group+json;v=2";
// fetch groups
export function fetchGroups(link: string) {
@@ -43,7 +43,7 @@ export function fetchGroupsByPage(link: string, page: number, filter?: string) {
// backend start counting by 0
if (filter) {
return fetchGroupsByLink(
`${link}?page=${page - 1}&q=${decodeURIComponent(filter)}`,
`${link}?page=${page - 1}&q=${decodeURIComponent(filter)}`
);
}
return fetchGroupsByLink(`${link}?page=${page - 1}`);
@@ -66,14 +66,14 @@ export function fetchGroupsByLink(link: string) {
export function fetchGroupsPending(): Action {
return {
type: FETCH_GROUPS_PENDING,
type: FETCH_GROUPS_PENDING
};
}
export function fetchGroupsSuccess(groups: any): Action {
return {
type: FETCH_GROUPS_SUCCESS,
payload: groups,
payload: groups
};
}
@@ -82,8 +82,8 @@ export function fetchGroupsFailure(url: string, error: Error): Action {
type: FETCH_GROUPS_FAILURE,
payload: {
error,
url,
},
url
}
};
}
@@ -93,7 +93,7 @@ export function fetchGroupByLink(group: Group) {
}
export function fetchGroupByName(link: string, name: string) {
const groupUrl = link.endsWith('/') ? link + name : link + '/' + name;
const groupUrl = link.endsWith("/") ? link + name : link + "/" + name;
return fetchGroup(groupUrl, name);
}
@@ -118,7 +118,7 @@ export function fetchGroupPending(name: string): Action {
return {
type: FETCH_GROUP_PENDING,
payload: name,
itemId: name,
itemId: name
};
}
@@ -126,7 +126,7 @@ export function fetchGroupSuccess(group: any): Action {
return {
type: FETCH_GROUP_SUCCESS,
payload: group,
itemId: group.name,
itemId: group.name
};
}
@@ -135,9 +135,9 @@ export function fetchGroupFailure(name: string, error: Error): Action {
type: FETCH_GROUP_FAILURE,
payload: {
name,
error,
error
},
itemId: name,
itemId: name
};
}
@@ -161,26 +161,26 @@ export function createGroup(link: string, group: Group, callback?: () => void) {
export function createGroupPending() {
return {
type: CREATE_GROUP_PENDING,
type: CREATE_GROUP_PENDING
};
}
export function createGroupSuccess() {
return {
type: CREATE_GROUP_SUCCESS,
type: CREATE_GROUP_SUCCESS
};
}
export function createGroupFailure(error: Error) {
return {
type: CREATE_GROUP_FAILURE,
payload: error,
payload: error
};
}
export function createGroupReset() {
return {
type: CREATE_GROUP_RESET,
type: CREATE_GROUP_RESET
};
}
@@ -209,7 +209,7 @@ export function modifyGroupPending(group: Group): Action {
return {
type: MODIFY_GROUP_PENDING,
payload: group,
itemId: group.name,
itemId: group.name
};
}
@@ -217,7 +217,7 @@ export function modifyGroupSuccess(group: Group): Action {
return {
type: MODIFY_GROUP_SUCCESS,
payload: group,
itemId: group.name,
itemId: group.name
};
}
@@ -226,16 +226,16 @@ export function modifyGroupFailure(group: Group, error: Error): Action {
type: MODIFY_GROUP_FAILURE,
payload: {
error,
group,
group
},
itemId: group.name,
itemId: group.name
};
}
export function modifyGroupReset(group: Group): Action {
return {
type: MODIFY_GROUP_RESET,
itemId: group.name,
itemId: group.name
};
}
@@ -262,7 +262,7 @@ export function deleteGroupPending(group: Group): Action {
return {
type: DELETE_GROUP_PENDING,
payload: group,
itemId: group.name,
itemId: group.name
};
}
@@ -270,7 +270,7 @@ export function deleteGroupSuccess(group: Group): Action {
return {
type: DELETE_GROUP_SUCCESS,
payload: group,
itemId: group.name,
itemId: group.name
};
}
@@ -279,9 +279,9 @@ export function deleteGroupFailure(group: Group, error: Error): Action {
type: DELETE_GROUP_FAILURE,
payload: {
error,
group,
group
},
itemId: group.name,
itemId: group.name
};
}
@@ -289,7 +289,7 @@ export function deleteGroupFailure(group: Group, error: Error): Action {
function extractGroupsByNames(
groups: Group[],
groupNames: string[],
oldGroupsByNames: object,
oldGroupsByNames: object
) {
const groupsByNames = {};
@@ -322,7 +322,7 @@ function deleteGroupInEntries(groups: [], groupName: string) {
const reducerByName = (state: any, groupname: string, newGroupState: any) => {
const newGroupsByNames = {
...state,
[groupname]: newGroupState,
[groupname]: newGroupState
};
return newGroupsByNames;
@@ -340,18 +340,18 @@ function listReducer(state: any = {}, action: any = {}) {
groupCreatePermission: !!action.payload._links.create,
page: action.payload.page,
pageTotal: action.payload.pageTotal,
_links: action.payload._links,
},
_links: action.payload._links
}
};
// Delete single group actions
case DELETE_GROUP_SUCCESS:
const newGroupEntries = deleteGroupInEntries(
state.entries,
action.payload.name,
action.payload.name
);
return {
...state,
entries: newGroupEntries,
entries: newGroupEntries
};
default:
return state;
@@ -366,14 +366,14 @@ function byNamesReducer(state: any = {}, action: any = {}) {
const groupNames = groups.map(group => group.name);
const byNames = extractGroupsByNames(groups, groupNames, state.byNames);
return {
...byNames,
...byNames
};
case FETCH_GROUP_SUCCESS:
return reducerByName(state, action.payload.name, action.payload);
case DELETE_GROUP_SUCCESS:
const newGroupByNames = deleteGroupInGroupsByNames(
state,
action.payload.name,
action.payload.name
);
return newGroupByNames;
@@ -384,7 +384,7 @@ function byNamesReducer(state: any = {}, action: any = {}) {
export default combineReducers({
list: listReducer,
byNames: byNamesReducer,
byNames: byNamesReducer
});
// selectors

View File

@@ -1,10 +1,10 @@
import i18n from 'i18next';
import Backend from 'i18next-fetch-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { reactI18nextModule } from 'react-i18next';
import { urls } from '@scm-manager/ui-components';
import i18n from "i18next";
import Backend from "i18next-fetch-backend";
import LanguageDetector from "i18next-browser-languagedetector";
import { reactI18nextModule } from "react-i18next";
import { urls } from "@scm-manager/ui-components";
const loadPath = urls.withContextPath('/locales/{{lng}}/{{ns}}.json');
const loadPath = urls.withContextPath("/locales/{{lng}}/{{ns}}.json");
// TODO load locales for moment
@@ -13,31 +13,31 @@ i18n
.use(LanguageDetector)
.use(reactI18nextModule)
.init({
fallbackLng: 'en',
fallbackLng: "en",
// try to load only "en" and not "en_US"
load: 'languageOnly',
load: "languageOnly",
// have a common namespace used around the full app
ns: ['commons'],
defaultNS: 'commons',
ns: ["commons"],
defaultNS: "commons",
debug: false,
interpolation: {
escapeValue: false, // not needed for react!!
escapeValue: false // not needed for react!!
},
react: {
wait: true,
wait: true
},
backend: {
loadPath: loadPath,
init: {
credentials: 'same-origin',
},
},
credentials: "same-origin"
}
}
});
export default i18n;

View File

@@ -1,22 +1,22 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Index from './containers/Index';
import React from "react";
import ReactDOM from "react-dom";
import Index from "./containers/Index";
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
import { I18nextProvider } from "react-i18next";
import i18n from "./i18n";
import { Provider } from 'react-redux';
import { Provider } from "react-redux";
import createReduxStore from './createReduxStore';
import { BrowserRouter as Router } from 'react-router-dom';
import createReduxStore from "./createReduxStore";
import { BrowserRouter as Router } from "react-router-dom";
import { urls } from '@scm-manager/ui-components';
import { urls } from "@scm-manager/ui-components";
const store = createReduxStore();
const root = document.getElementById('root');
const root = document.getElementById("root");
if (!root) {
throw new Error('could not find root element');
throw new Error("could not find root element");
}
ReactDOM.render(
@@ -27,5 +27,5 @@ ReactDOM.render(
</Router>
</I18nextProvider>
</Provider>,
root,
root
);

View File

@@ -29,62 +29,62 @@ import reducer, {
LOGOUT_REDIRECT,
LOGOUT_SUCCESS,
logoutSuccess,
redirectAfterLogout,
} from './auth';
redirectAfterLogout
} from "./auth";
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 {
FETCH_INDEXRESOURCES_PENDING,
FETCH_INDEXRESOURCES_SUCCESS,
} from './indexResource';
FETCH_INDEXRESOURCES_SUCCESS
} from "./indexResource";
const me = {
name: 'tricia',
displayName: 'Tricia McMillian',
mail: 'trillian@heartofgold.universe',
name: "tricia",
displayName: "Tricia McMillian",
mail: "trillian@heartofgold.universe"
};
describe('auth reducer', () => {
it('should set me and login on successful fetch of me', () => {
describe("auth reducer", () => {
it("should set me and login on successful fetch of me", () => {
const state = reducer(undefined, fetchMeSuccess(me));
expect(state.me).toBe(me);
});
it('should set authenticated to false', () => {
it("should set authenticated to false", () => {
const initialState = {
me,
me
};
const state = reducer(initialState, fetchMeUnauthenticated());
expect(state.me.name).toBeUndefined();
});
it('should reset the state after logout', () => {
it("should reset the state after logout", () => {
const initialState = {
me,
me
};
const state = reducer(initialState, logoutSuccess());
expect(state.me).toBeUndefined();
expect(state.authenticated).toBeUndefined();
});
it('should keep state and set redirecting to true', () => {
it("should keep state and set redirecting to true", () => {
const initialState = {
me,
me
};
const state = reducer(initialState, redirectAfterLogout());
expect(state.me).toBe(initialState.me);
expect(state.redirecting).toBe(true);
});
it('should set state authenticated and me after login', () => {
it("should set state authenticated and me after login", () => {
const state = reducer(undefined, loginSuccess(me));
expect(state.me).toBe(me);
});
});
describe('auth actions', () => {
describe("auth actions", () => {
const mockStore = configureMockStore([thunk]);
afterEach(() => {
@@ -92,72 +92,72 @@ describe('auth actions', () => {
fetchMock.restore();
});
it('should dispatch login success and dispatch fetch me', () => {
fetchMock.postOnce('/api/v2/auth/access_token', {
it("should dispatch login success and dispatch fetch me", () => {
fetchMock.postOnce("/api/v2/auth/access_token", {
body: {
cookie: true,
grant_type: 'password',
username: 'tricia',
password: 'secret123',
grant_type: "password",
username: "tricia",
password: "secret123"
},
headers: {
'content-type': 'application/json',
},
"content-type": "application/json"
}
});
fetchMock.getOnce('/api/v2/me', {
fetchMock.getOnce("/api/v2/me", {
body: me,
headers: {
'content-type': 'application/json',
},
"content-type": "application/json"
}
});
const meLink = {
me: {
href: '/me',
},
href: "/me"
}
};
fetchMock.getOnce('/api/v2/', {
_links: meLink,
fetchMock.getOnce("/api/v2/", {
_links: meLink
});
const expectedActions = [
{
type: LOGIN_PENDING,
type: LOGIN_PENDING
},
{
type: FETCH_INDEXRESOURCES_PENDING,
type: FETCH_INDEXRESOURCES_PENDING
},
{
type: FETCH_INDEXRESOURCES_SUCCESS,
payload: {
_links: meLink,
},
_links: meLink
}
},
{
type: LOGIN_SUCCESS,
payload: me,
},
payload: me
}
];
const store = mockStore({});
return store
.dispatch(login('/auth/access_token', 'tricia', 'secret123'))
.dispatch(login("/auth/access_token", "tricia", "secret123"))
.then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
it('should dispatch login failure', () => {
fetchMock.postOnce('/api/v2/auth/access_token', {
status: 400,
it("should dispatch login failure", () => {
fetchMock.postOnce("/api/v2/auth/access_token", {
status: 400
});
const store = mockStore({});
return store
.dispatch(login('/auth/access_token', 'tricia', 'secret123'))
.dispatch(login("/auth/access_token", "tricia", "secret123"))
.then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(LOGIN_PENDING);
@@ -166,38 +166,38 @@ describe('auth actions', () => {
});
});
it('should dispatch fetch me success', () => {
fetchMock.getOnce('/api/v2/me', {
it("should dispatch fetch me success", () => {
fetchMock.getOnce("/api/v2/me", {
body: me,
headers: {
'content-type': 'application/json',
},
"content-type": "application/json"
}
});
const expectedActions = [
{
type: FETCH_ME_PENDING,
type: FETCH_ME_PENDING
},
{
type: FETCH_ME_SUCCESS,
payload: me,
},
payload: me
}
];
const store = mockStore({});
return store.dispatch(fetchMe('me')).then(() => {
return store.dispatch(fetchMe("me")).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
it('should dispatch fetch me failure', () => {
fetchMock.getOnce('/api/v2/me', {
status: 500,
it("should dispatch fetch me failure", () => {
fetchMock.getOnce("/api/v2/me", {
status: 500
});
const store = mockStore({});
return store.dispatch(fetchMe('me')).then(() => {
return store.dispatch(fetchMe("me")).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(FETCH_ME_PENDING);
expect(actions[1].type).toEqual(FETCH_ME_FAILURE);
@@ -205,111 +205,111 @@ describe('auth actions', () => {
});
});
it('should dispatch fetch me unauthorized', () => {
fetchMock.getOnce('/api/v2/me', 401);
it("should dispatch fetch me unauthorized", () => {
fetchMock.getOnce("/api/v2/me", 401);
const expectedActions = [
{
type: FETCH_ME_PENDING,
type: FETCH_ME_PENDING
},
{
type: FETCH_ME_UNAUTHORIZED,
resetPending: true,
},
resetPending: true
}
];
const store = mockStore({});
return store.dispatch(fetchMe('me')).then(() => {
return store.dispatch(fetchMe("me")).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions);
});
});
it('should dispatch logout success', () => {
fetchMock.deleteOnce('/api/v2/auth/access_token', {
status: 204,
it("should dispatch logout success", () => {
fetchMock.deleteOnce("/api/v2/auth/access_token", {
status: 204
});
fetchMock.getOnce('/api/v2/me', {
status: 401,
fetchMock.getOnce("/api/v2/me", {
status: 401
});
fetchMock.getOnce('/api/v2/', {
fetchMock.getOnce("/api/v2/", {
_links: {
login: {
login: '/login',
},
},
login: "/login"
}
}
});
const expectedActions = [
{
type: LOGOUT_PENDING,
type: LOGOUT_PENDING
},
{
type: LOGOUT_SUCCESS,
type: LOGOUT_SUCCESS
},
{
type: FETCH_INDEXRESOURCES_PENDING,
},
type: FETCH_INDEXRESOURCES_PENDING
}
];
const store = mockStore({});
return store.dispatch(logout('/auth/access_token')).then(() => {
return store.dispatch(logout("/auth/access_token")).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
it('should dispatch logout success and redirect', () => {
fetchMock.deleteOnce('/api/v2/auth/access_token', {
it("should dispatch logout success and redirect", () => {
fetchMock.deleteOnce("/api/v2/auth/access_token", {
status: 200,
body: {
logoutRedirect: 'http://example.com/cas/logout',
},
logoutRedirect: "http://example.com/cas/logout"
}
});
fetchMock.getOnce('/api/v2/me', {
status: 401,
fetchMock.getOnce("/api/v2/me", {
status: 401
});
fetchMock.getOnce('/api/v2/', {
fetchMock.getOnce("/api/v2/", {
_links: {
login: {
login: '/login',
},
},
login: "/login"
}
}
});
window.location.assign = jest.fn();
const expectedActions = [
{
type: LOGOUT_PENDING,
type: LOGOUT_PENDING
},
{
type: LOGOUT_REDIRECT,
},
type: LOGOUT_REDIRECT
}
];
const store = mockStore({});
return store.dispatch(logout('/auth/access_token')).then(() => {
return store.dispatch(logout("/auth/access_token")).then(() => {
expect(window.location.assign.mock.calls[0][0]).toBe(
'http://example.com/cas/logout',
"http://example.com/cas/logout"
);
expect(store.getActions()).toEqual(expectedActions);
});
});
it('should dispatch logout failure', () => {
fetchMock.deleteOnce('/api/v2/auth/access_token', {
status: 500,
it("should dispatch logout failure", () => {
fetchMock.deleteOnce("/api/v2/auth/access_token", {
status: 500
});
const store = mockStore({});
return store.dispatch(logout('/auth/access_token')).then(() => {
return store.dispatch(logout("/auth/access_token")).then(() => {
const actions = store.getActions();
expect(actions[0].type).toEqual(LOGOUT_PENDING);
expect(actions[1].type).toEqual(LOGOUT_FAILURE);
@@ -318,170 +318,170 @@ describe('auth actions', () => {
});
});
describe('auth selectors', () => {
const error = new Error('yo it failed');
describe("auth selectors", () => {
const error = new Error("yo it failed");
it('should return true if me exist and login Link does not exist', () => {
it("should return true if me exist and login Link does not exist", () => {
expect(
isAuthenticated({
auth: {
me,
me
},
indexResources: {
links: {},
},
}),
links: {}
}
})
).toBe(true);
});
it('should return false if me exist and login Link does exist', () => {
it("should return false if me exist and login Link does exist", () => {
expect(
isAuthenticated({
auth: {
me,
me
},
indexResources: {
links: {
login: {
href: 'login.href',
},
},
},
}),
href: "login.href"
}
}
}
})
).toBe(false);
});
it('should return me', () => {
it("should return me", () => {
expect(
getMe({
auth: {
me,
},
}),
me
}
})
).toBe(me);
});
it('should return undefined, if me is not set', () => {
it("should return undefined, if me is not set", () => {
expect(getMe({})).toBeUndefined();
});
it('should return true, if FETCH_ME is pending', () => {
it("should return true, if FETCH_ME is pending", () => {
expect(
isFetchMePending({
pending: {
[FETCH_ME]: true,
},
}),
[FETCH_ME]: true
}
})
).toBe(true);
});
it('should return false, if FETCH_ME is not in pending state', () => {
it("should return false, if FETCH_ME is not in pending state", () => {
expect(
isFetchMePending({
pending: {},
}),
pending: {}
})
).toBe(false);
});
it('should return true, if LOGIN is pending', () => {
it("should return true, if LOGIN is pending", () => {
expect(
isLoginPending({
pending: {
[LOGIN]: true,
},
}),
[LOGIN]: true
}
})
).toBe(true);
});
it('should return false, if LOGIN is not in pending state', () => {
it("should return false, if LOGIN is not in pending state", () => {
expect(
isLoginPending({
pending: {},
}),
pending: {}
})
).toBe(false);
});
it('should return true, if LOGOUT is pending', () => {
it("should return true, if LOGOUT is pending", () => {
expect(
isLogoutPending({
pending: {
[LOGOUT]: true,
},
}),
[LOGOUT]: true
}
})
).toBe(true);
});
it('should return false, if LOGOUT is not in pending state', () => {
it("should return false, if LOGOUT is not in pending state", () => {
expect(
isLogoutPending({
pending: {},
}),
pending: {}
})
).toBe(false);
});
it('should return the error, if failure state is set for FETCH_ME', () => {
it("should return the error, if failure state is set for FETCH_ME", () => {
expect(
getFetchMeFailure({
failure: {
[FETCH_ME]: error,
},
}),
[FETCH_ME]: error
}
})
).toBe(error);
});
it('should return unknown, if failure state is not set for FETCH_ME', () => {
it("should return unknown, if failure state is not set for FETCH_ME", () => {
expect(getFetchMeFailure({})).toBeUndefined();
});
it('should return the error, if failure state is set for LOGIN', () => {
it("should return the error, if failure state is set for LOGIN", () => {
expect(
getLoginFailure({
failure: {
[LOGIN]: error,
},
}),
[LOGIN]: error
}
})
).toBe(error);
});
it('should return unknown, if failure state is not set for LOGIN', () => {
it("should return unknown, if failure state is not set for LOGIN", () => {
expect(getLoginFailure({})).toBeUndefined();
});
it('should return the error, if failure state is set for LOGOUT', () => {
it("should return the error, if failure state is set for LOGOUT", () => {
expect(
getLogoutFailure({
failure: {
[LOGOUT]: error,
},
}),
[LOGOUT]: error
}
})
).toBe(error);
});
it('should return unknown, if failure state is not set for LOGOUT', () => {
it("should return unknown, if failure state is not set for LOGOUT", () => {
expect(getLogoutFailure({})).toBeUndefined();
});
it('should return false, if redirecting is not set', () => {
it("should return false, if redirecting is not set", () => {
expect(isRedirecting({})).toBe(false);
});
it('should return false, if redirecting is false', () => {
it("should return false, if redirecting is false", () => {
expect(
isRedirecting({
auth: {
redirecting: false,
},
}),
redirecting: false
}
})
).toBe(false);
});
it('should return true, if redirecting is true', () => {
it("should return true, if redirecting is true", () => {
expect(
isRedirecting({
auth: {
redirecting: true,
},
}),
redirecting: true
}
})
).toBe(true);
});
});

View File

@@ -1,31 +1,31 @@
import { Me } from '@scm-manager/ui-types';
import * as types from './types';
import { Me } from "@scm-manager/ui-types";
import * as types from "./types";
import { apiClient, UnauthorizedError } from '@scm-manager/ui-components';
import { isPending } from './pending';
import { getFailure } from './failure';
import { apiClient, UnauthorizedError } from "@scm-manager/ui-components";
import { isPending } from "./pending";
import { getFailure } from "./failure";
import {
callFetchIndexResources,
fetchIndexResources,
fetchIndexResourcesPending,
fetchIndexResourcesSuccess,
getLoginLink,
} from './indexResource';
getLoginLink
} from "./indexResource";
// Action
export const LOGIN = 'scm/auth/LOGIN';
export const LOGIN = "scm/auth/LOGIN";
export const LOGIN_PENDING = `${LOGIN}_${types.PENDING_SUFFIX}`;
export const LOGIN_SUCCESS = `${LOGIN}_${types.SUCCESS_SUFFIX}`;
export const LOGIN_FAILURE = `${LOGIN}_${types.FAILURE_SUFFIX}`;
export const FETCH_ME = 'scm/auth/FETCH_ME';
export const FETCH_ME = "scm/auth/FETCH_ME";
export const FETCH_ME_PENDING = `${FETCH_ME}_${types.PENDING_SUFFIX}`;
export const FETCH_ME_SUCCESS = `${FETCH_ME}_${types.SUCCESS_SUFFIX}`;
export const FETCH_ME_FAILURE = `${FETCH_ME}_${types.FAILURE_SUFFIX}`;
export const FETCH_ME_UNAUTHORIZED = `${FETCH_ME}_UNAUTHORIZED`;
export const LOGOUT = 'scm/auth/LOGOUT';
export const LOGOUT = "scm/auth/LOGOUT";
export const LOGOUT_PENDING = `${LOGOUT}_${types.PENDING_SUFFIX}`;
export const LOGOUT_SUCCESS = `${LOGOUT}_${types.SUCCESS_SUFFIX}`;
export const LOGOUT_FAILURE = `${LOGOUT}_${types.FAILURE_SUFFIX}`;
@@ -38,19 +38,19 @@ const initialState = {};
export default function reducer(
state: object = initialState,
action: object = {
type: 'UNKNOWN',
},
type: "UNKNOWN"
}
) {
switch (action.type) {
case LOGIN_SUCCESS:
case FETCH_ME_SUCCESS:
return {
...state,
me: action.payload,
me: action.payload
};
case FETCH_ME_UNAUTHORIZED:
return {
me: {},
me: {}
};
case LOGOUT_SUCCESS:
return initialState;
@@ -59,7 +59,7 @@ export default function reducer(
// we keep the current state until we are redirected to the new page
return {
...state,
redirecting: true,
redirecting: true
};
}
default:
@@ -71,73 +71,73 @@ export default function reducer(
export const loginPending = () => {
return {
type: LOGIN_PENDING,
type: LOGIN_PENDING
};
};
export const loginSuccess = (me: Me) => {
return {
type: LOGIN_SUCCESS,
payload: me,
payload: me
};
};
export const loginFailure = (error: Error) => {
return {
type: LOGIN_FAILURE,
payload: error,
payload: error
};
};
export const logoutPending = () => {
return {
type: LOGOUT_PENDING,
type: LOGOUT_PENDING
};
};
export const logoutSuccess = () => {
return {
type: LOGOUT_SUCCESS,
type: LOGOUT_SUCCESS
};
};
export const redirectAfterLogout = () => {
return {
type: LOGOUT_REDIRECT,
type: LOGOUT_REDIRECT
};
};
export const logoutFailure = (error: Error) => {
return {
type: LOGOUT_FAILURE,
payload: error,
payload: error
};
};
export const fetchMePending = () => {
return {
type: FETCH_ME_PENDING,
type: FETCH_ME_PENDING
};
};
export const fetchMeSuccess = (me: Me) => {
return {
type: FETCH_ME_SUCCESS,
payload: me,
payload: me
};
};
export const fetchMeUnauthenticated = () => {
return {
type: FETCH_ME_UNAUTHORIZED,
resetPending: true,
resetPending: true
};
};
export const fetchMeFailure = (error: Error) => {
return {
type: FETCH_ME_FAILURE,
payload: error,
payload: error
};
};
@@ -152,13 +152,13 @@ const callFetchMe = (link: string): Promise<Me> => {
export const login = (
loginLink: string,
username: string,
password: string,
password: string
) => {
const login_data = {
cookie: true,
grant_type: 'password',
grant_type: "password",
username,
password,
password
};
return function(dispatch: any) {
dispatch(loginPending());

View File

@@ -1,27 +1,27 @@
import fetchMock from 'fetch-mock';
import { changePassword, CONTENT_TYPE_PASSWORD_CHANGE } from './changePassword';
import fetchMock from "fetch-mock";
import { changePassword, CONTENT_TYPE_PASSWORD_CHANGE } from "./changePassword";
describe('change password', () => {
const CHANGE_PASSWORD_URL = '/me/password';
const oldPassword = 'old';
const newPassword = 'new';
describe("change password", () => {
const CHANGE_PASSWORD_URL = "/me/password";
const oldPassword = "old";
const newPassword = "new";
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
it('should update password', done => {
fetchMock.put('/api/v2' + CHANGE_PASSWORD_URL, 204, {
it("should update password", done => {
fetchMock.put("/api/v2" + CHANGE_PASSWORD_URL, 204, {
headers: {
'content-type': CONTENT_TYPE_PASSWORD_CHANGE,
},
"content-type": CONTENT_TYPE_PASSWORD_CHANGE
}
});
changePassword(CHANGE_PASSWORD_URL, oldPassword, newPassword).then(
content => {
done();
},
}
);
});
});

View File

@@ -1,20 +1,20 @@
import { apiClient } from '@scm-manager/ui-components';
import { apiClient } from "@scm-manager/ui-components";
export const CONTENT_TYPE_PASSWORD_CHANGE =
'application/vnd.scmm-passwordChange+json;v=2';
"application/vnd.scmm-passwordChange+json;v=2";
export function changePassword(
url: string,
oldPassword: string,
newPassword: string,
newPassword: string
) {
return apiClient
.put(
url,
{
oldPassword,
newPassword,
newPassword
},
CONTENT_TYPE_PASSWORD_CHANGE,
CONTENT_TYPE_PASSWORD_CHANGE
)
.then(response => {
return response;

View File

@@ -1,158 +1,158 @@
import reducer, { getFailure } from './failure';
import reducer, { getFailure } from "./failure";
const err = new Error('something failed');
const otherErr = new Error('something else failed');
const err = new Error("something failed");
const otherErr = new Error("something else failed");
describe('failure reducer', () => {
it('should set the error for FETCH_ITEMS', () => {
describe("failure reducer", () => {
it("should set the error for FETCH_ITEMS", () => {
const newState = reducer(
{},
{
type: 'FETCH_ITEMS_FAILURE',
payload: err,
},
type: "FETCH_ITEMS_FAILURE",
payload: err
}
);
expect(newState['FETCH_ITEMS']).toBe(err);
expect(newState["FETCH_ITEMS"]).toBe(err);
});
it('should do nothing for unknown action types', () => {
it("should do nothing for unknown action types", () => {
const state = {};
const newState = reducer(state, {
type: 'UNKNOWN',
type: "UNKNOWN"
});
expect(newState).toBe(state);
});
it('should set the error for FETCH_ITEMS, if payload has multiple values', () => {
it("should set the error for FETCH_ITEMS, if payload has multiple values", () => {
const newState = reducer(
{},
{
type: 'FETCH_ITEMS_FAILURE',
type: "FETCH_ITEMS_FAILURE",
payload: {
something: 'something',
error: err,
},
},
something: "something",
error: err
}
}
);
expect(newState['FETCH_ITEMS']).toBe(err);
expect(newState["FETCH_ITEMS"]).toBe(err);
});
it('should set the error for FETCH_ITEMS, but should not affect others', () => {
it("should set the error for FETCH_ITEMS, but should not affect others", () => {
const newState = reducer(
{
FETCH_USERS: otherErr,
FETCH_USERS: otherErr
},
{
type: 'FETCH_ITEMS_FAILURE',
payload: err,
},
type: "FETCH_ITEMS_FAILURE",
payload: err
}
);
expect(newState['FETCH_ITEMS']).toBe(err);
expect(newState['FETCH_USERS']).toBe(otherErr);
expect(newState["FETCH_ITEMS"]).toBe(err);
expect(newState["FETCH_USERS"]).toBe(otherErr);
});
it('should reset FETCH_ITEMS after FETCH_ITEMS_SUCCESS', () => {
it("should reset FETCH_ITEMS after FETCH_ITEMS_SUCCESS", () => {
const newState = reducer(
{
FETCH_ITEMS: err
},
{
type: "FETCH_ITEMS_SUCCESS"
}
);
expect(newState["FETCH_ITEMS"]).toBeFalsy();
});
it("should reset FETCH_ITEMS after FETCH_ITEMS_RESET", () => {
const newState = reducer(
{
FETCH_ITEMS: err
},
{
type: "FETCH_ITEMS_RESET"
}
);
expect(newState["FETCH_ITEMS"]).toBeFalsy();
});
it("should reset FETCH_ITEMS after FETCH_ITEMS_RESET, but should not affect others", () => {
const newState = reducer(
{
FETCH_ITEMS: err,
FETCH_USERS: err
},
{
type: 'FETCH_ITEMS_SUCCESS',
},
type: "FETCH_ITEMS_RESET"
}
);
expect(newState['FETCH_ITEMS']).toBeFalsy();
expect(newState["FETCH_ITEMS"]).toBeFalsy();
expect(newState["FETCH_USERS"]).toBe(err);
});
it('should reset FETCH_ITEMS after FETCH_ITEMS_RESET', () => {
const newState = reducer(
{
FETCH_ITEMS: err,
},
{
type: 'FETCH_ITEMS_RESET',
},
);
expect(newState['FETCH_ITEMS']).toBeFalsy();
});
it('should reset FETCH_ITEMS after FETCH_ITEMS_RESET, but should not affect others', () => {
const newState = reducer(
{
FETCH_ITEMS: err,
FETCH_USERS: err,
},
{
type: 'FETCH_ITEMS_RESET',
},
);
expect(newState['FETCH_ITEMS']).toBeFalsy();
expect(newState['FETCH_USERS']).toBe(err);
});
it('should set the error for a single item of FETCH_ITEM', () => {
it("should set the error for a single item of FETCH_ITEM", () => {
const newState = reducer(
{},
{
type: 'FETCH_ITEM_FAILURE',
type: "FETCH_ITEM_FAILURE",
payload: err,
itemId: 42,
},
itemId: 42
}
);
expect(newState['FETCH_ITEM/42']).toBe(err);
expect(newState["FETCH_ITEM/42"]).toBe(err);
});
it('should reset error for a single item of FETCH_ITEM', () => {
it("should reset error for a single item of FETCH_ITEM", () => {
const newState = reducer(
{
'FETCH_ITEM/42': err,
"FETCH_ITEM/42": err
},
{
type: 'FETCH_ITEM_SUCCESS',
type: "FETCH_ITEM_SUCCESS",
payload: err,
itemId: 42,
},
itemId: 42
}
);
expect(newState['FETCH_ITEM/42']).toBeUndefined();
expect(newState["FETCH_ITEM/42"]).toBeUndefined();
});
});
describe('failure selector', () => {
it('should return failure, if FETCH_ITEMS failure exists', () => {
describe("failure selector", () => {
it("should return failure, if FETCH_ITEMS failure exists", () => {
const failure = getFailure(
{
failure: {
FETCH_ITEMS: err,
},
FETCH_ITEMS: err
}
},
'FETCH_ITEMS',
"FETCH_ITEMS"
);
expect(failure).toBe(err);
});
it('should return undefined, if state has no failure', () => {
const failure = getFailure({}, 'FETCH_ITEMS');
it("should return undefined, if state has no failure", () => {
const failure = getFailure({}, "FETCH_ITEMS");
expect(failure).toBeUndefined();
});
it('should return undefined, if FETCH_ITEMS is not defined', () => {
it("should return undefined, if FETCH_ITEMS is not defined", () => {
const failure = getFailure(
{
failure: {},
failure: {}
},
'FETCH_ITEMS',
"FETCH_ITEMS"
);
expect(failure).toBeFalsy();
});
it('should return failure, if FETCH_ITEM 42 failure exists', () => {
it("should return failure, if FETCH_ITEM 42 failure exists", () => {
const failure = getFailure(
{
failure: {
'FETCH_ITEM/42': err,
},
"FETCH_ITEM/42": err
}
},
'FETCH_ITEM',
42,
"FETCH_ITEM",
42
);
expect(failure).toBe(err);
});

View File

@@ -1,13 +1,13 @@
import { Action } from '@scm-manager/ui-types';
import { Action } from "@scm-manager/ui-types";
const FAILURE_SUFFIX = '_FAILURE';
const FAILURE_SUFFIX = "_FAILURE";
const RESET_PATTERN = /^(.*)_(SUCCESS|RESET)$/;
function extractIdentifierFromFailure(action: Action) {
const type = action.type;
let identifier = type.substring(0, type.length - FAILURE_SUFFIX.length);
if (action.itemId) {
identifier += '/' + action.itemId;
identifier += "/" + action.itemId;
}
return identifier;
}
@@ -15,7 +15,7 @@ function extractIdentifierFromFailure(action: Action) {
function removeAllEntriesOfIdentifierFromState(
state: object,
payload: any,
identifier: string,
identifier: string
) {
const newState = {};
for (let failureType in state) {
@@ -39,8 +39,8 @@ function removeFromState(state: object, identifier: string) {
export default function reducer(
state: object = {},
action: Action = {
type: 'UNKNOWN',
},
type: "UNKNOWN"
}
): object {
const type = action.type;
if (type.endsWith(FAILURE_SUFFIX)) {
@@ -53,20 +53,20 @@ export default function reducer(
}
return {
...state,
[identifier]: payload,
[identifier]: payload
};
} else {
const match = RESET_PATTERN.exec(type);
if (match) {
let identifier = match[1];
if (action.itemId) {
identifier += '/' + action.itemId;
identifier += "/" + action.itemId;
}
if (action.payload)
return removeAllEntriesOfIdentifierFromState(
state,
action.payload,
identifier,
identifier
);
else return removeFromState(state, identifier);
}
@@ -77,12 +77,12 @@ export default function reducer(
export function getFailure(
state: object,
actionType: string,
itemId?: string | number,
itemId?: string | number
) {
if (state.failure) {
let identifier = actionType;
if (itemId) {
identifier += '/' + itemId;
identifier += "/" + itemId;
}
return state.failure[identifier];
}

View File

@@ -1,6 +1,6 @@
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_INDEXRESOURCES_PENDING,
FETCH_INDEXRESOURCES_SUCCESS,
@@ -24,76 +24,76 @@ import reducer, {
getGroupsLink,
getLinkCollection,
getUserAutoCompleteLink,
getGroupAutoCompleteLink,
} from './indexResource';
getGroupAutoCompleteLink
} from "./indexResource";
const indexResourcesUnauthenticated = {
version: '2.0.0-SNAPSHOT',
version: "2.0.0-SNAPSHOT",
_links: {
self: {
href: 'http://localhost:8081/scm/api/v2/',
href: "http://localhost:8081/scm/api/v2/"
},
uiPlugins: {
href: 'http://localhost:8081/scm/api/v2/ui/plugins',
href: "http://localhost:8081/scm/api/v2/ui/plugins"
},
login: {
href: 'http://localhost:8081/scm/api/v2/auth/access_token',
},
},
href: "http://localhost:8081/scm/api/v2/auth/access_token"
}
}
};
const indexResourcesAuthenticated = {
version: '2.0.0-SNAPSHOT',
version: "2.0.0-SNAPSHOT",
_links: {
self: {
href: 'http://localhost:8081/scm/api/v2/',
href: "http://localhost:8081/scm/api/v2/"
},
uiPlugins: {
href: 'http://localhost:8081/scm/api/v2/ui/plugins',
href: "http://localhost:8081/scm/api/v2/ui/plugins"
},
me: {
href: 'http://localhost:8081/scm/api/v2/me/',
href: "http://localhost:8081/scm/api/v2/me/"
},
logout: {
href: 'http://localhost:8081/scm/api/v2/auth/access_token',
href: "http://localhost:8081/scm/api/v2/auth/access_token"
},
users: {
href: 'http://localhost:8081/scm/api/v2/users/',
href: "http://localhost:8081/scm/api/v2/users/"
},
groups: {
href: 'http://localhost:8081/scm/api/v2/groups/',
href: "http://localhost:8081/scm/api/v2/groups/"
},
config: {
href: 'http://localhost:8081/scm/api/v2/config',
href: "http://localhost:8081/scm/api/v2/config"
},
repositories: {
href: 'http://localhost:8081/scm/api/v2/repositories/',
href: "http://localhost:8081/scm/api/v2/repositories/"
},
hgConfig: {
href: 'http://localhost:8081/scm/api/v2/config/hg',
href: "http://localhost:8081/scm/api/v2/config/hg"
},
gitConfig: {
href: 'http://localhost:8081/scm/api/v2/config/git',
href: "http://localhost:8081/scm/api/v2/config/git"
},
svnConfig: {
href: 'http://localhost:8081/scm/api/v2/config/svn',
href: "http://localhost:8081/scm/api/v2/config/svn"
},
autocomplete: [
{
href: 'http://localhost:8081/scm/api/v2/autocomplete/users',
name: 'users',
href: "http://localhost:8081/scm/api/v2/autocomplete/users",
name: "users"
},
{
href: 'http://localhost:8081/scm/api/v2/autocomplete/groups',
name: 'groups',
},
],
},
href: "http://localhost:8081/scm/api/v2/autocomplete/groups",
name: "groups"
}
]
}
};
describe('index resource', () => {
describe('fetch index resource', () => {
const index_url = '/api/v2/';
describe("index resource", () => {
describe("fetch index resource", () => {
const index_url = "/api/v2/";
const mockStore = configureMockStore([thunk]);
afterEach(() => {
@@ -101,17 +101,17 @@ describe('index resource', () => {
fetchMock.restore();
});
it('should successfully fetch index resources when unauthenticated', () => {
it("should successfully fetch index resources when unauthenticated", () => {
fetchMock.getOnce(index_url, indexResourcesUnauthenticated);
const expectedActions = [
{
type: FETCH_INDEXRESOURCES_PENDING,
type: FETCH_INDEXRESOURCES_PENDING
},
{
type: FETCH_INDEXRESOURCES_SUCCESS,
payload: indexResourcesUnauthenticated,
},
payload: indexResourcesUnauthenticated
}
];
const store = mockStore({});
@@ -120,17 +120,17 @@ describe('index resource', () => {
});
});
it('should successfully fetch index resources when authenticated', () => {
it("should successfully fetch index resources when authenticated", () => {
fetchMock.getOnce(index_url, indexResourcesAuthenticated);
const expectedActions = [
{
type: FETCH_INDEXRESOURCES_PENDING,
type: FETCH_INDEXRESOURCES_PENDING
},
{
type: FETCH_INDEXRESOURCES_SUCCESS,
payload: indexResourcesAuthenticated,
},
payload: indexResourcesAuthenticated
}
];
const store = mockStore({});
@@ -139,9 +139,9 @@ describe('index resource', () => {
});
});
it('should dispatch FETCH_INDEX_RESOURCES_FAILURE if request fails', () => {
it("should dispatch FETCH_INDEX_RESOURCES_FAILURE if request fails", () => {
fetchMock.getOnce(index_url, {
status: 500,
status: 500
});
const store = mockStore({});
@@ -154,338 +154,338 @@ describe('index resource', () => {
});
});
describe('index resources reducer', () => {
it('should return empty object, if state and action is undefined', () => {
describe("index resources reducer", () => {
it("should return empty object, if state and action is undefined", () => {
expect(reducer()).toEqual({});
});
it('should return the same state, if the action is undefined', () => {
it("should return the same state, if the action is undefined", () => {
const state = {
x: true,
x: true
};
expect(reducer(state)).toBe(state);
});
it('should return the same state, if the action is unknown to the reducer', () => {
it("should return the same state, if the action is unknown to the reducer", () => {
const state = {
x: true,
x: true
};
expect(
reducer(state, {
type: 'EL_SPECIALE',
}),
type: "EL_SPECIALE"
})
).toBe(state);
});
it('should store the index resources on FETCH_INDEXRESOURCES_SUCCESS', () => {
it("should store the index resources on FETCH_INDEXRESOURCES_SUCCESS", () => {
const newState = reducer(
{},
fetchIndexResourcesSuccess(indexResourcesAuthenticated),
fetchIndexResourcesSuccess(indexResourcesAuthenticated)
);
expect(newState.links).toBe(indexResourcesAuthenticated._links);
});
});
describe('index resources selectors', () => {
const error = new Error('something goes wrong');
describe("index resources selectors", () => {
const error = new Error("something goes wrong");
it('should return true, when fetch index resources is pending', () => {
it("should return true, when fetch index resources is pending", () => {
const state = {
pending: {
[FETCH_INDEXRESOURCES]: true,
},
[FETCH_INDEXRESOURCES]: true
}
};
expect(isFetchIndexResourcesPending(state)).toEqual(true);
});
it('should return false, when fetch index resources is not pending', () => {
it("should return false, when fetch index resources is not pending", () => {
expect(isFetchIndexResourcesPending({})).toEqual(false);
});
it('should return error when fetch index resources did fail', () => {
it("should return error when fetch index resources did fail", () => {
const state = {
failure: {
[FETCH_INDEXRESOURCES]: error,
},
[FETCH_INDEXRESOURCES]: error
}
};
expect(getFetchIndexResourcesFailure(state)).toEqual(error);
});
it('should return undefined when fetch index resources did not fail', () => {
it("should return undefined when fetch index resources did not fail", () => {
expect(getFetchIndexResourcesFailure({})).toBe(undefined);
});
it('should return all links', () => {
it("should return all links", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getLinks(state)).toBe(indexResourcesAuthenticated._links);
});
// ui plugins link
it('should return ui plugins link when authenticated and has permission to see it', () => {
it("should return ui plugins link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getUiPluginsLink(state)).toBe(
'http://localhost:8081/scm/api/v2/ui/plugins',
"http://localhost:8081/scm/api/v2/ui/plugins"
);
});
it('should return ui plugins links when unauthenticated and has permission to see it', () => {
it("should return ui plugins links when unauthenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getUiPluginsLink(state)).toBe(
'http://localhost:8081/scm/api/v2/ui/plugins',
"http://localhost:8081/scm/api/v2/ui/plugins"
);
});
// me link
it('should return me link when authenticated and has permission to see it', () => {
it("should return me link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getMeLink(state)).toBe('http://localhost:8081/scm/api/v2/me/');
expect(getMeLink(state)).toBe("http://localhost:8081/scm/api/v2/me/");
});
it('should return undefined for me link when unauthenticated or has not permission to see it', () => {
it("should return undefined for me link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getMeLink(state)).toBe(undefined);
});
// logout link
it('should return logout link when authenticated and has permission to see it', () => {
it("should return logout link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getLogoutLink(state)).toBe(
'http://localhost:8081/scm/api/v2/auth/access_token',
"http://localhost:8081/scm/api/v2/auth/access_token"
);
});
it('should return undefined for logout link when unauthenticated or has not permission to see it', () => {
it("should return undefined for logout link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getLogoutLink(state)).toBe(undefined);
});
// login link
it('should return login link when unauthenticated and has permission to see it', () => {
it("should return login link when unauthenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getLoginLink(state)).toBe(
'http://localhost:8081/scm/api/v2/auth/access_token',
"http://localhost:8081/scm/api/v2/auth/access_token"
);
});
it('should return undefined for login link when authenticated or has not permission to see it', () => {
it("should return undefined for login link when authenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getLoginLink(state)).toBe(undefined);
});
// users link
it('should return users link when authenticated and has permission to see it', () => {
it("should return users link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getUsersLink(state)).toBe(
'http://localhost:8081/scm/api/v2/users/',
"http://localhost:8081/scm/api/v2/users/"
);
});
it('should return undefined for users link when unauthenticated or has not permission to see it', () => {
it("should return undefined for users link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getUsersLink(state)).toBe(undefined);
});
// groups link
it('should return groups link when authenticated and has permission to see it', () => {
it("should return groups link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getGroupsLink(state)).toBe(
'http://localhost:8081/scm/api/v2/groups/',
"http://localhost:8081/scm/api/v2/groups/"
);
});
it('should return undefined for groups link when unauthenticated or has not permission to see it', () => {
it("should return undefined for groups link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getGroupsLink(state)).toBe(undefined);
});
// config link
it('should return config link when authenticated and has permission to see it', () => {
it("should return config link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getConfigLink(state)).toBe(
'http://localhost:8081/scm/api/v2/config',
"http://localhost:8081/scm/api/v2/config"
);
});
it('should return undefined for config link when unauthenticated or has not permission to see it', () => {
it("should return undefined for config link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getConfigLink(state)).toBe(undefined);
});
// repositories link
it('should return repositories link when authenticated and has permission to see it', () => {
it("should return repositories link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getRepositoriesLink(state)).toBe(
'http://localhost:8081/scm/api/v2/repositories/',
"http://localhost:8081/scm/api/v2/repositories/"
);
});
it('should return config for repositories link when unauthenticated or has not permission to see it', () => {
it("should return config for repositories link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getRepositoriesLink(state)).toBe(undefined);
});
// hgConfig link
it('should return hgConfig link when authenticated and has permission to see it', () => {
it("should return hgConfig link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getHgConfigLink(state)).toBe(
'http://localhost:8081/scm/api/v2/config/hg',
"http://localhost:8081/scm/api/v2/config/hg"
);
});
it('should return config for hgConfig link when unauthenticated or has not permission to see it', () => {
it("should return config for hgConfig link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getHgConfigLink(state)).toBe(undefined);
});
// gitConfig link
it('should return gitConfig link when authenticated and has permission to see it', () => {
it("should return gitConfig link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getGitConfigLink(state)).toBe(
'http://localhost:8081/scm/api/v2/config/git',
"http://localhost:8081/scm/api/v2/config/git"
);
});
it('should return config for gitConfig link when unauthenticated or has not permission to see it', () => {
it("should return config for gitConfig link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getGitConfigLink(state)).toBe(undefined);
});
// svnConfig link
it('should return svnConfig link when authenticated and has permission to see it', () => {
it("should return svnConfig link when authenticated and has permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getSvnConfigLink(state)).toBe(
'http://localhost:8081/scm/api/v2/config/svn',
"http://localhost:8081/scm/api/v2/config/svn"
);
});
it('should return config for svnConfig link when unauthenticated or has not permission to see it', () => {
it("should return config for svnConfig link when unauthenticated or has not permission to see it", () => {
const state = {
indexResources: {
links: indexResourcesUnauthenticated._links,
},
links: indexResourcesUnauthenticated._links
}
};
expect(getSvnConfigLink(state)).toBe(undefined);
});
// Autocomplete links
it('should return link collection', () => {
it("should return link collection", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getLinkCollection(state, 'autocomplete')).toEqual(
indexResourcesAuthenticated._links.autocomplete,
expect(getLinkCollection(state, "autocomplete")).toEqual(
indexResourcesAuthenticated._links.autocomplete
);
});
it('should return user autocomplete link', () => {
it("should return user autocomplete link", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getUserAutoCompleteLink(state)).toEqual(
'http://localhost:8081/scm/api/v2/autocomplete/users',
"http://localhost:8081/scm/api/v2/autocomplete/users"
);
});
it('should return group autocomplete link', () => {
it("should return group autocomplete link", () => {
const state = {
indexResources: {
links: indexResourcesAuthenticated._links,
},
links: indexResourcesAuthenticated._links
}
};
expect(getGroupAutoCompleteLink(state)).toEqual(
'http://localhost:8081/scm/api/v2/autocomplete/groups',
"http://localhost:8081/scm/api/v2/autocomplete/groups"
);
});
});

View File

@@ -1,18 +1,18 @@
import * as types from './types';
import * as types from "./types";
import { apiClient } from '@scm-manager/ui-components';
import { Action, IndexResources, Link } from '@scm-manager/ui-types';
import { isPending } from './pending';
import { getFailure } from './failure';
import { apiClient } from "@scm-manager/ui-components";
import { Action, IndexResources, Link } from "@scm-manager/ui-types";
import { isPending } from "./pending";
import { getFailure } from "./failure";
// Action
export const FETCH_INDEXRESOURCES = 'scm/INDEXRESOURCES';
export const FETCH_INDEXRESOURCES = "scm/INDEXRESOURCES";
export const FETCH_INDEXRESOURCES_PENDING = `${FETCH_INDEXRESOURCES}_${types.PENDING_SUFFIX}`;
export const FETCH_INDEXRESOURCES_SUCCESS = `${FETCH_INDEXRESOURCES}_${types.SUCCESS_SUFFIX}`;
export const FETCH_INDEXRESOURCES_FAILURE = `${FETCH_INDEXRESOURCES}_${types.FAILURE_SUFFIX}`;
const INDEX_RESOURCES_LINK = '/';
const INDEX_RESOURCES_LINK = "/";
export const callFetchIndexResources = (): Promise<IndexResources> => {
return apiClient.get(INDEX_RESOURCES_LINK).then(response => {
@@ -35,21 +35,21 @@ export function fetchIndexResources() {
export function fetchIndexResourcesPending(): Action {
return {
type: FETCH_INDEXRESOURCES_PENDING,
type: FETCH_INDEXRESOURCES_PENDING
};
}
export function fetchIndexResourcesSuccess(resources: IndexResources): Action {
return {
type: FETCH_INDEXRESOURCES_SUCCESS,
payload: resources,
payload: resources
};
}
export function fetchIndexResourcesFailure(err: Error): Action {
return {
type: FETCH_INDEXRESOURCES_FAILURE,
payload: err,
payload: err
};
}
@@ -57,8 +57,8 @@ export function fetchIndexResourcesFailure(err: Error): Action {
export default function reducer(
state: object = {},
action: Action = {
type: 'UNKNOWN',
},
type: "UNKNOWN"
}
): object {
if (!action.payload) {
return state;
@@ -69,7 +69,7 @@ export default function reducer(
return {
...state,
version: action.payload.version,
links: action.payload._links,
links: action.payload._links
};
default:
return state;
@@ -108,89 +108,89 @@ export function getAppVersion(state: object) {
}
export function getUiPluginsLink(state: object) {
return getLink(state, 'uiPlugins');
return getLink(state, "uiPlugins");
}
export function getAvailablePluginsLink(state: object) {
return getLink(state, 'availablePlugins');
return getLink(state, "availablePlugins");
}
export function getInstalledPluginsLink(state: object) {
return getLink(state, 'installedPlugins');
return getLink(state, "installedPlugins");
}
export function getPendingPluginsLink(state: object) {
return getLink(state, 'pendingPlugins');
return getLink(state, "pendingPlugins");
}
export function getMeLink(state: object) {
return getLink(state, 'me');
return getLink(state, "me");
}
export function getLogoutLink(state: object) {
return getLink(state, 'logout');
return getLink(state, "logout");
}
export function getLoginLink(state: object) {
return getLink(state, 'login');
return getLink(state, "login");
}
export function getUsersLink(state: object) {
return getLink(state, 'users');
return getLink(state, "users");
}
export function getRepositoryRolesLink(state: object) {
return getLink(state, 'repositoryRoles');
return getLink(state, "repositoryRoles");
}
export function getRepositoryVerbsLink(state: object) {
return getLink(state, 'repositoryVerbs');
return getLink(state, "repositoryVerbs");
}
export function getGroupsLink(state: object) {
return getLink(state, 'groups');
return getLink(state, "groups");
}
export function getConfigLink(state: object) {
return getLink(state, 'config');
return getLink(state, "config");
}
export function getRepositoriesLink(state: object) {
return getLink(state, 'repositories');
return getLink(state, "repositories");
}
export function getHgConfigLink(state: object) {
return getLink(state, 'hgConfig');
return getLink(state, "hgConfig");
}
export function getGitConfigLink(state: object) {
return getLink(state, 'gitConfig');
return getLink(state, "gitConfig");
}
export function getSvnConfigLink(state: object) {
return getLink(state, 'svnConfig');
return getLink(state, "svnConfig");
}
export function getLoginInfoLink(state: object) {
return getLink(state, 'loginInfo');
return getLink(state, "loginInfo");
}
export function getUserAutoCompleteLink(state: object): string {
const link = getLinkCollection(state, 'autocomplete').find(
i => i.name === 'users',
const link = getLinkCollection(state, "autocomplete").find(
i => i.name === "users"
);
if (link) {
return link.href;
}
return '';
return "";
}
export function getGroupAutoCompleteLink(state: object): string {
const link = getLinkCollection(state, 'autocomplete').find(
i => i.name === 'groups',
const link = getLinkCollection(state, "autocomplete").find(
i => i.name === "groups"
);
if (link) {
return link.href;
}
return '';
return "";
}

View File

@@ -1,168 +1,168 @@
import reducer, { isPending } from './pending';
import reducer, { isPending } from "./pending";
describe('pending reducer', () => {
it('should set pending for FETCH_ITEMS to true', () => {
describe("pending reducer", () => {
it("should set pending for FETCH_ITEMS to true", () => {
const newState = reducer(
{},
{
type: 'FETCH_ITEMS_PENDING',
},
type: "FETCH_ITEMS_PENDING"
}
);
expect(newState['FETCH_ITEMS']).toBe(true);
expect(newState["FETCH_ITEMS"]).toBe(true);
});
it('should do nothing for unknown action types', () => {
it("should do nothing for unknown action types", () => {
const state = {};
const newState = reducer(state, {
type: 'UNKNOWN',
type: "UNKNOWN"
});
expect(newState).toBe(state);
});
it('should set pending for FETCH_ITEMS to true, but should not affect others', () => {
it("should set pending for FETCH_ITEMS to true, but should not affect others", () => {
const newState = reducer(
{
FETCH_USERS: true
},
{
type: "FETCH_ITEMS_PENDING"
}
);
expect(newState["FETCH_ITEMS"]).toBe(true);
expect(newState["FETCH_USERS"]).toBe(true);
});
it("should reset pending state for FETCH_ITEMS after FETCH_ITEMS_SUCCESS", () => {
const newState = reducer(
{
FETCH_ITEMS: true
},
{
type: "FETCH_ITEMS_SUCCESS"
}
);
expect(newState["FETCH_ITEMS"]).toBeFalsy();
});
it("should reset pending state for FETCH_ITEMS after FETCH_ITEMS_FAILURE", () => {
const newState = reducer(
{
FETCH_ITEMS: true
},
{
type: "FETCH_ITEMS_FAILURE"
}
);
expect(newState["FETCH_ITEMS"]).toBeFalsy();
});
it("should reset pending state for FETCH_ITEMS after FETCH_ITEMS_RESET", () => {
const newState = reducer(
{
FETCH_ITEMS: true
},
{
type: "FETCH_ITEMS_RESET"
}
);
expect(newState["FETCH_ITEMS"]).toBeFalsy();
});
it("should reset pending state for FETCH_ITEMS, if resetPending prop is available", () => {
const newState = reducer(
{
FETCH_ITEMS: true
},
{
type: "FETCH_ITEMS_SOMETHING",
resetPending: true
}
);
expect(newState["FETCH_ITEMS"]).toBeFalsy();
});
it("should reset pending state for FETCH_ITEMS after FETCH_ITEMS_SUCCESS, but should not affect others", () => {
const newState = reducer(
{
FETCH_USERS: true,
FETCH_ITEMS: true
},
{
type: 'FETCH_ITEMS_PENDING',
},
type: "FETCH_ITEMS_SUCCESS"
}
);
expect(newState['FETCH_ITEMS']).toBe(true);
expect(newState['FETCH_USERS']).toBe(true);
expect(newState["FETCH_ITEMS"]).toBeFalsy();
expect(newState["FETCH_USERS"]).toBe(true);
});
it('should reset pending state for FETCH_ITEMS after FETCH_ITEMS_SUCCESS', () => {
it("should set pending for a single item", () => {
const newState = reducer(
{
FETCH_ITEMS: true,
"FETCH_USER/42": false
},
{
type: 'FETCH_ITEMS_SUCCESS',
},
type: "FETCH_USER_PENDING",
itemId: 21
}
);
expect(newState['FETCH_ITEMS']).toBeFalsy();
expect(newState["FETCH_USER/21"]).toBe(true);
expect(newState["FETCH_USER/42"]).toBe(false);
});
it('should reset pending state for FETCH_ITEMS after FETCH_ITEMS_FAILURE', () => {
it("should reset pending for a single item", () => {
const newState = reducer(
{
FETCH_ITEMS: true,
"FETCH_USER/42": true
},
{
type: 'FETCH_ITEMS_FAILURE',
},
type: "FETCH_USER_SUCCESS",
itemId: 42
}
);
expect(newState['FETCH_ITEMS']).toBeFalsy();
});
it('should reset pending state for FETCH_ITEMS after FETCH_ITEMS_RESET', () => {
const newState = reducer(
{
FETCH_ITEMS: true,
},
{
type: 'FETCH_ITEMS_RESET',
},
);
expect(newState['FETCH_ITEMS']).toBeFalsy();
});
it('should reset pending state for FETCH_ITEMS, if resetPending prop is available', () => {
const newState = reducer(
{
FETCH_ITEMS: true,
},
{
type: 'FETCH_ITEMS_SOMETHING',
resetPending: true,
},
);
expect(newState['FETCH_ITEMS']).toBeFalsy();
});
it('should reset pending state for FETCH_ITEMS after FETCH_ITEMS_SUCCESS, but should not affect others', () => {
const newState = reducer(
{
FETCH_USERS: true,
FETCH_ITEMS: true,
},
{
type: 'FETCH_ITEMS_SUCCESS',
},
);
expect(newState['FETCH_ITEMS']).toBeFalsy();
expect(newState['FETCH_USERS']).toBe(true);
});
it('should set pending for a single item', () => {
const newState = reducer(
{
'FETCH_USER/42': false,
},
{
type: 'FETCH_USER_PENDING',
itemId: 21,
},
);
expect(newState['FETCH_USER/21']).toBe(true);
expect(newState['FETCH_USER/42']).toBe(false);
});
it('should reset pending for a single item', () => {
const newState = reducer(
{
'FETCH_USER/42': true,
},
{
type: 'FETCH_USER_SUCCESS',
itemId: 42,
},
);
expect(newState['FETCH_USER/42']).toBeFalsy();
expect(newState["FETCH_USER/42"]).toBeFalsy();
});
});
describe('pending selectors', () => {
it('should return true, while FETCH_ITEMS is pending', () => {
describe("pending selectors", () => {
it("should return true, while FETCH_ITEMS is pending", () => {
const result = isPending(
{
pending: {
FETCH_ITEMS: true,
},
FETCH_ITEMS: true
}
},
'FETCH_ITEMS',
"FETCH_ITEMS"
);
expect(result).toBe(true);
});
it('should return false, if pending is not defined', () => {
const result = isPending({}, 'FETCH_ITEMS');
it("should return false, if pending is not defined", () => {
const result = isPending({}, "FETCH_ITEMS");
expect(result).toBe(false);
});
it('should return true, while FETCH_ITEM 42 is pending', () => {
it("should return true, while FETCH_ITEM 42 is pending", () => {
const result = isPending(
{
pending: {
'FETCH_ITEM/42': true,
},
"FETCH_ITEM/42": true
}
},
'FETCH_ITEM',
42,
"FETCH_ITEM",
42
);
expect(result).toBe(true);
});
it('should return true, while FETCH_ITEM 42 is undefined', () => {
it("should return true, while FETCH_ITEM 42 is undefined", () => {
const result = isPending(
{
pending: {
'FETCH_ITEM/21': true,
},
"FETCH_ITEM/21": true
}
},
'FETCH_ITEM',
42,
"FETCH_ITEM",
42
);
expect(result).toBe(false);
});

View File

@@ -1,11 +1,11 @@
import { Action } from '@scm-manager/ui-types';
import * as types from './types';
import { Action } from "@scm-manager/ui-types";
import * as types from "./types";
const PENDING_SUFFIX = '_' + types.PENDING_SUFFIX;
const PENDING_SUFFIX = "_" + types.PENDING_SUFFIX;
const RESET_ACTIONTYPES = [
types.SUCCESS_SUFFIX,
types.FAILURE_SUFFIX,
types.RESET_SUFFIX,
types.RESET_SUFFIX
];
function removeFromState(state: object, identifier: string) {
@@ -21,7 +21,7 @@ function removeFromState(state: object, identifier: string) {
function removeAllEntriesOfIdentifierFromState(
state: object,
payload: any,
identifier: string,
identifier: string
) {
const newState = {};
for (let childType in state) {
@@ -36,7 +36,7 @@ function extractIdentifierFromPending(action: Action) {
const type = action.type;
let identifier = type.substring(0, type.length - PENDING_SUFFIX.length);
if (action.itemId) {
identifier += '/' + action.itemId;
identifier += "/" + action.itemId;
}
return identifier;
}
@@ -44,30 +44,30 @@ function extractIdentifierFromPending(action: Action) {
export default function reducer(
state: object = {},
action: Action = {
type: 'UNKNOWN',
},
type: "UNKNOWN"
}
): object {
const type = action.type;
if (type.endsWith(PENDING_SUFFIX)) {
const identifier = extractIdentifierFromPending(action);
return {
...state,
[identifier]: true,
[identifier]: true
};
} else {
const index = type.lastIndexOf('_');
const index = type.lastIndexOf("_");
if (index > 0) {
const actionType = type.substring(index + 1);
if (RESET_ACTIONTYPES.indexOf(actionType) >= 0 || action.resetPending) {
let identifier = type.substring(0, index);
if (action.itemId) {
identifier += '/' + action.itemId;
identifier += "/" + action.itemId;
}
if (action.payload)
return removeAllEntriesOfIdentifierFromState(
state,
action.payload,
identifier,
identifier
);
else return removeFromState(state, identifier);
}
@@ -79,11 +79,11 @@ export default function reducer(
export function isPending(
state: object,
actionType: string,
itemId?: string | number,
itemId?: string | number
) {
let type = actionType;
if (itemId) {
type += '/' + itemId;
type += "/" + itemId;
}
if (state.pending && state.pending[type]) {
return true;

View File

@@ -1,4 +1,4 @@
export const PENDING_SUFFIX = 'PENDING';
export const SUCCESS_SUFFIX = 'SUCCESS';
export const FAILURE_SUFFIX = 'FAILURE';
export const RESET_SUFFIX = 'RESET';
export const PENDING_SUFFIX = "PENDING";
export const SUCCESS_SUFFIX = "SUCCESS";
export const FAILURE_SUFFIX = "FAILURE";
export const RESET_SUFFIX = "RESET";

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { translate } from 'react-i18next';
import { Checkbox } from '@scm-manager/ui-components';
import React from "react";
import { translate } from "react-i18next";
import { Checkbox } from "@scm-manager/ui-components";
type Props = {
permission: string;
@@ -13,20 +13,20 @@ type Props = {
class PermissionCheckbox extends React.Component<Props> {
render() {
const { t, permission, checked, onChange, disabled } = this.props;
const key = permission.split(':').join('.');
const key = permission.split(":").join(".");
return (
<Checkbox
name={permission}
label={this.translateOrDefault(
'permissions.' + key + '.displayName',
key,
"permissions." + key + ".displayName",
key
)}
checked={checked}
onChange={onChange}
disabled={disabled}
helpText={this.translateOrDefault(
'permissions.' + key + '.description',
t('permissions.unknown'),
"permissions." + key + ".description",
t("permissions.unknown")
)}
/>
);
@@ -42,4 +42,4 @@ class PermissionCheckbox extends React.Component<Props> {
};
}
export default translate('plugins')(PermissionCheckbox);
export default translate("plugins")(PermissionCheckbox);

View File

@@ -1,17 +1,17 @@
import React from 'react';
import { connect } from 'react-redux';
import { translate } from 'react-i18next';
import classNames from 'classnames';
import styled from 'styled-components';
import { Link } from '@scm-manager/ui-types';
import React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import classNames from "classnames";
import styled from "styled-components";
import { Link } from "@scm-manager/ui-types";
import {
Notification,
ErrorNotification,
SubmitButton,
} from '@scm-manager/ui-components';
import { getLink } from '../../modules/indexResource';
import { loadPermissionsForEntity, setPermissions } from './handlePermissions';
import PermissionCheckbox from './PermissionCheckbox';
SubmitButton
} from "@scm-manager/ui-components";
import { getLink } from "../../modules/indexResource";
import { loadPermissionsForEntity, setPermissions } from "./handlePermissions";
import PermissionCheckbox from "./PermissionCheckbox";
type Props = {
availablePermissionLink: string;
@@ -51,20 +51,20 @@ class SetPermissions extends React.Component<Props, State> {
permissionsChanged: false,
permissionsSubmitted: false,
modifiable: false,
overwritePermissionsLink: undefined,
overwritePermissionsLink: undefined
};
}
setLoadingState = () => {
this.setState({
loading: true,
loading: true
});
};
setErrorState = (error: Error) => {
this.setState({
error: error,
loading: false,
loading: false
});
};
@@ -73,20 +73,20 @@ class SetPermissions extends React.Component<Props, State> {
loading: false,
error: undefined,
permissionsSubmitted: true,
permissionsChanged: false,
permissionsChanged: false
});
};
componentDidMount(): void {
loadPermissionsForEntity(
this.props.availablePermissionLink,
this.props.selectedPermissionsLink.href,
this.props.selectedPermissionsLink.href
).then(response => {
const { permissions, overwriteLink } = response;
this.setState({
permissions: permissions,
loading: false,
overwritePermissionsLink: overwriteLink,
overwritePermissionsLink: overwriteLink
});
});
}
@@ -102,7 +102,7 @@ class SetPermissions extends React.Component<Props, State> {
if (this.state.overwritePermissionsLink) {
setPermissions(
this.state.overwritePermissionsLink.href,
selectedPermissions,
selectedPermissions
)
.then(result => {
this.setSuccessfulState();
@@ -123,8 +123,8 @@ class SetPermissions extends React.Component<Props, State> {
if (permissionsSubmitted) {
message = (
<Notification
type={'success'}
children={t('setPermissions.setPermissionsSuccessful')}
type={"success"}
children={t("setPermissions.setPermissionsSuccessful")}
onClose={() => this.onClose()}
/>
);
@@ -139,7 +139,7 @@ class SetPermissions extends React.Component<Props, State> {
<SubmitButton
disabled={!this.state.permissionsChanged}
loading={loading}
label={t('setPermissions.button')}
label={t("setPermissions.button")}
/>
</form>
);
@@ -150,7 +150,7 @@ class SetPermissions extends React.Component<Props, State> {
const permissionArray = Object.keys(permissions);
return (
<div className="columns">
<PermissionsWrapper className={classNames('column', 'is-half')}>
<PermissionsWrapper className={classNames("column", "is-half")}>
{permissionArray.slice(0, permissionArray.length / 2 + 1).map(p => (
<PermissionCheckbox
key={p}
@@ -161,7 +161,7 @@ class SetPermissions extends React.Component<Props, State> {
/>
))}
</PermissionsWrapper>
<PermissionsWrapper className={classNames('column', 'is-half')}>
<PermissionsWrapper className={classNames("column", "is-half")}>
{permissionArray
.slice(permissionArray.length / 2 + 1, permissionArray.length)
.map(p => (
@@ -184,25 +184,25 @@ class SetPermissions extends React.Component<Props, State> {
newPermissions[name] = value;
return {
permissions: newPermissions,
permissionsChanged: true,
permissionsChanged: true
};
});
};
onClose = () => {
this.setState({
permissionsSubmitted: false,
permissionsSubmitted: false
});
};
}
const mapStateToProps = state => {
const availablePermissionLink = getLink(state, 'permissions');
const availablePermissionLink = getLink(state, "permissions");
return {
availablePermissionLink,
availablePermissionLink
};
};
export default connect(mapStateToProps)(
translate('permissions')(SetPermissions),
translate("permissions")(SetPermissions)
);

View File

@@ -1,9 +1,9 @@
import fetchMock from 'fetch-mock';
import { loadPermissionsForEntity } from './handlePermissions';
import fetchMock from "fetch-mock";
import { loadPermissionsForEntity } from "./handlePermissions";
describe('load permissions for entity', () => {
const AVAILABLE_PERMISSIONS_URL = '/permissions';
const USER_PERMISSIONS_URL = '/user/scmadmin/permissions';
describe("load permissions for entity", () => {
const AVAILABLE_PERMISSIONS_URL = "/permissions";
const USER_PERMISSIONS_URL = "/user/scmadmin/permissions";
const availablePermissions = `{
"permissions": [
@@ -28,10 +28,10 @@ describe('load permissions for entity', () => {
beforeEach(() => {
fetchMock.getOnce(
'/api/v2' + AVAILABLE_PERMISSIONS_URL,
availablePermissions,
"/api/v2" + AVAILABLE_PERMISSIONS_URL,
availablePermissions
);
fetchMock.getOnce('/api/v2' + USER_PERMISSIONS_URL, userPermissions);
fetchMock.getOnce("/api/v2" + USER_PERMISSIONS_URL, userPermissions);
});
afterEach(() => {
@@ -39,27 +39,27 @@ describe('load permissions for entity', () => {
fetchMock.restore();
});
it('should return permissions array', done => {
it("should return permissions array", done => {
loadPermissionsForEntity(
AVAILABLE_PERMISSIONS_URL,
USER_PERMISSIONS_URL,
USER_PERMISSIONS_URL
).then(result => {
const { permissions } = result;
expect(Object.entries(permissions).length).toBe(3);
expect(permissions['repository:read,pull:*']).toBe(true);
expect(permissions['repository:read,pull,push:*']).toBe(false);
expect(permissions['repository:*:*']).toBe(false);
expect(permissions["repository:read,pull:*"]).toBe(true);
expect(permissions["repository:read,pull,push:*"]).toBe(false);
expect(permissions["repository:*:*"]).toBe(false);
done();
});
});
it('should return overwrite link', done => {
it("should return overwrite link", done => {
loadPermissionsForEntity(
AVAILABLE_PERMISSIONS_URL,
USER_PERMISSIONS_URL,
USER_PERMISSIONS_URL
).then(result => {
const { overwriteLink } = result;
expect(overwriteLink.href).toBe('/api/v2/users/rene/permissions');
expect(overwriteLink.href).toBe("/api/v2/users/rene/permissions");
done();
});
});

View File

@@ -1,16 +1,16 @@
import { apiClient } from '@scm-manager/ui-components';
import { apiClient } from "@scm-manager/ui-components";
export const CONTENT_TYPE_PERMISSIONS =
'application/vnd.scmm-permissionCollection+json;v=2';
"application/vnd.scmm-permissionCollection+json;v=2";
export function setPermissions(url: string, permissions: string[]) {
return apiClient
.put(
url,
{
permissions: permissions,
permissions: permissions
},
CONTENT_TYPE_PERMISSIONS,
CONTENT_TYPE_PERMISSIONS
)
.then(response => {
return response;
@@ -19,7 +19,7 @@ export function setPermissions(url: string, permissions: string[]) {
export function loadPermissionsForEntity(
availableUrl: string,
userUrl: string,
userUrl: string
) {
return Promise.all([
apiClient.get(availableUrl).then(response => {
@@ -27,7 +27,7 @@ export function loadPermissionsForEntity(
}),
apiClient.get(userUrl).then(response => {
return response.json();
}),
})
]).then(values => {
const [availablePermissions, checkedPermissions] = values;
const permissions = {};
@@ -35,7 +35,7 @@ export function loadPermissionsForEntity(
checkedPermissions.permissions.forEach(p => (permissions[p] = true));
return {
permissions,
overwriteLink: checkedPermissions._links.overwrite,
overwriteLink: checkedPermissions._links.overwrite
};
});
}

Some files were not shown because too many files have changed in this diff Show More