diff --git a/scm-ui/ui-webapp/public/locales/de/namespaces.json b/scm-ui/ui-webapp/public/locales/de/namespaces.json new file mode 100644 index 0000000000..7a687de4ff --- /dev/null +++ b/scm-ui/ui-webapp/public/locales/de/namespaces.json @@ -0,0 +1,9 @@ +{ + "namespaceRoot": { + "menu": { + "navigationLabel": "Namespace", + "settingsNavLink": "Einstellungen", + "permissionsNavLink": "Berechtigungen" + } + } +} diff --git a/scm-ui/ui-webapp/public/locales/en/namespaces.json b/scm-ui/ui-webapp/public/locales/en/namespaces.json new file mode 100644 index 0000000000..a4b99c39ee --- /dev/null +++ b/scm-ui/ui-webapp/public/locales/en/namespaces.json @@ -0,0 +1,9 @@ +{ + "namespaceRoot": { + "menu": { + "navigationLabel": "Namespace", + "settingsNavLink": "Settings", + "permissionsNavLink": "Permissions" + } + } +} diff --git a/scm-ui/ui-webapp/src/containers/Main.tsx b/scm-ui/ui-webapp/src/containers/Main.tsx index 48e6f37a76..e0af6ee867 100644 --- a/scm-ui/ui-webapp/src/containers/Main.tsx +++ b/scm-ui/ui-webapp/src/containers/Main.tsx @@ -46,6 +46,7 @@ import CreateGroup from "../groups/containers/CreateGroup"; import Admin from "../admin/containers/Admin"; import Profile from "./Profile"; +import NamespaceRoot from "../repos/namespaces/containers/NamespaceRoot"; type Props = { me: Me; @@ -80,6 +81,7 @@ class Main extends React.Component { + diff --git a/scm-ui/ui-webapp/src/repos/modules/repos.ts b/scm-ui/ui-webapp/src/repos/modules/repos.ts index a4dc9d2e8c..a38dcbb76f 100644 --- a/scm-ui/ui-webapp/src/repos/modules/repos.ts +++ b/scm-ui/ui-webapp/src/repos/modules/repos.ts @@ -25,7 +25,7 @@ import { apiClient } from "@scm-manager/ui-components"; import * as types from "../../modules/types"; import { - Action, + Action, Namespace, NamespaceCollection, Repository, RepositoryCollection, @@ -66,6 +66,11 @@ export const DELETE_REPO_PENDING = `${DELETE_REPO}_${types.PENDING_SUFFIX}`; export const DELETE_REPO_SUCCESS = `${DELETE_REPO}_${types.SUCCESS_SUFFIX}`; export const DELETE_REPO_FAILURE = `${DELETE_REPO}_${types.FAILURE_SUFFIX}`; +export const FETCH_NAMESPACE = "scm/repos/FETCH_NAMESPACE"; +export const FETCH_NAMESPACE_PENDING = `${FETCH_NAMESPACE}_${types.PENDING_SUFFIX}`; +export const FETCH_NAMESPACE_SUCCESS = `${FETCH_NAMESPACE}_${types.SUCCESS_SUFFIX}`; +export const FETCH_NAMESPACE_FAILURE = `${FETCH_NAMESPACE}_${types.FAILURE_SUFFIX}`; + export const CONTENT_TYPE = "application/vnd.scmm-repository+json;v=2"; export const CUSTOM_NAMESPACE_STRATEGY = "CustomNamespaceStrategy"; @@ -388,6 +393,50 @@ export function deleteRepoFailure(repository: Repository, error: Error): Action }; } +export function fetchNamespace(link: string, namespaceName: string) { + return function(dispatch: any) { + dispatch(fetchNamespacePending(namespaceName)); + return apiClient + .get(link) + .then(response => response.json()) + .then(namespace => { + dispatch(fetchNamespaceSuccess(namespace)); + }) + .catch(err => { + dispatch(fetchNamespaceFailure(namespaceName, err)); + }); + }; +} + +export function fetchNamespacePending(namespaceName: string): Action { + return { + type: FETCH_NAMESPACE_PENDING, + payload: { + namespaceName + }, + itemId: namespaceName + }; +} + +export function fetchNamespaceSuccess(namespace: Namespace): Action { + return { + type: FETCH_NAMESPACE_SUCCESS, + payload: namespace, + itemId: namespace.namespace + }; +} + +export function fetchNamespaceFailure(namespaceName: string, error: Error): Action { + return { + type: FETCH_NAMESPACE_FAILURE, + payload: { + namespaceName, + error + }, + itemId: namespaceName + }; +} + // reducer function createIdentifier(repository: Repository) { @@ -425,6 +474,17 @@ const reducerByNames = (state: object, repository: Repository) => { }; }; +const reducerForNamespace = (state: object, namespace: Namespace) => { + const identifier = namespace.namespace; + return { + ...state, + namespacesByNames: { + ...state.namespacesByNames, + [identifier]: namespace + } + }; +}; + const reducerForNamespaces = (state: object, namespaces: NamespaceCollection) => { return { ...state, @@ -449,6 +509,8 @@ export default function reducer( return reducerForNamespaces(state, action.payload); case FETCH_REPO_SUCCESS: return reducerByNames(state, action.payload); + case FETCH_NAMESPACE_SUCCESS: + return reducerForNamespace(state, action.payload); default: return state; } @@ -497,10 +559,17 @@ export function getFetchNamespacesFailure(state: object) { return getFailure(state, FETCH_NAMESPACES); } -export function getNamespace(state: object, namespace: string) { - if (state.namespaces) { - return state.namespaces[namespace]; - } +export function isFetchNamespacePending(state: object) { + return isPending(state, FETCH_NAMESPACE); +} + +export function getFetchNamespaceFailure(state: object) { + return getFailure(state, FETCH_NAMESPACE); +} + +export function fetchNamespaceByName(link: string, namespaceName: string) { + const namespaceUrl = link.endsWith("/") ? link : link + "/"; + return fetchNamespace(`${namespaceUrl}${namespaceName}`, namespaceName); } export function isFetchRepoPending(state: object, namespace: string, name: string) { @@ -511,6 +580,12 @@ export function getFetchRepoFailure(state: object, namespace: string, name: stri return getFailure(state, FETCH_REPO, namespace + "/" + name); } +export function getNamespace(state: object, namespaceName: string) { + if (state.repos && state.repos.namespacesByNames) { + return state.repos.namespacesByNames[namespaceName]; + } +} + export function isAbleToCreateRepos(state: object) { return !!(state.repos && state.repos.list && state.repos.list._links && state.repos.list._links.create); } @@ -539,7 +614,12 @@ export function getDeleteRepoFailure(state: object, namespace: string, name: str return getFailure(state, DELETE_REPO, namespace + "/" + name); } -export function getPermissionsLink(state: object, namespace: string, name: string) { - const repo = getRepository(state, namespace, name); - return repo && repo._links ? repo._links.permissions.href : undefined; +export function getPermissionsLink(state: object, namespaceName: string, repoName?: string) { + if (repoName) { + const repo = getRepository(state, namespaceName, repoName); + return repo && repo._links ? repo._links.permissions.href : undefined; + } else { + const namespace = getNamespace(state, namespaceName); + return namespace && namespace._links ? namespace._links.permissions.href : undefined; + } } diff --git a/scm-ui/ui-webapp/src/repos/namespaces/containers/NamespaceRoot.tsx b/scm-ui/ui-webapp/src/repos/namespaces/containers/NamespaceRoot.tsx new file mode 100644 index 0000000000..a3cbb89610 --- /dev/null +++ b/scm-ui/ui-webapp/src/repos/namespaces/containers/NamespaceRoot.tsx @@ -0,0 +1,145 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from "react"; +import { WithTranslation, withTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { Redirect, Route, RouteComponentProps, Switch } from "react-router-dom"; +import { fetchNamespaceByName, getNamespace, isFetchNamespacePending } from "../../modules/repos"; +import { getNamespacesLink } from "../../../modules/indexResource"; +import { Namespace } from "@scm-manager/ui-types"; +import { + CustomQueryFlexWrappedColumns, + ErrorPage, + Loading, + Page, PrimaryContentColumn, + SecondaryNavigation, + SecondaryNavigationColumn, + StateMenuContextProvider, + SubNavigation +} from "@scm-manager/ui-components"; +import Permissions from "../../permissions/containers/Permissions"; +import { ExtensionPoint } from "@scm-manager/ui-extensions"; +import PermissionsNavLink from "./PermissionsNavLink"; + +type Props = RouteComponentProps & + WithTranslation & { + loading: boolean; + namespaceName: string; + namespacesLink: string; + namespace: Namespace; + + // dispatch functions + fetchNamespace: (link: string, namespace: string) => void; + }; + +class NamespaceRoot extends React.Component { + componentDidMount() { + const { namespacesLink, namespaceName, fetchNamespace } = this.props; + fetchNamespace(namespacesLink, namespaceName); + } + + stripEndingSlash = (url: string) => { + if (url.endsWith("/")) { + return url.substring(0, url.length - 1); + } + return url; + }; + + matchedUrl = () => { + return this.stripEndingSlash(this.props.match.url); + }; + + render() { + const { loading, error, namespaceName, namespace, t } = this.props; + const url = this.matchedUrl(); + + const extensionProps = { + namespace, + url + }; + + if (error) { + return ( + + ); + } + + if (!namespace || loading) { + return ; + } + + return ( + + + + + + + { + return ; + }} + /> + + + + + + + + + + + + + + + + ); + } +} + +const mapStateToProps = (state: any, ownProps: Props) => { + const { namespaceName } = ownProps.match.params; + const namespacesLink = getNamespacesLink(state); + const namespace = getNamespace(state, namespaceName); + const loading = isFetchNamespacePending(state); + return { namespaceName, namespacesLink, loading, namespace }; +}; + +const mapDispatchToProps = (dispatch: any) => { + return { + fetchNamespace: (link: string, namespaceName: string) => { + dispatch(fetchNamespaceByName(link, namespaceName)); + } + }; +}; + +export default connect(mapStateToProps, mapDispatchToProps)(withTranslation("namespaces")(NamespaceRoot)); diff --git a/scm-ui/ui-webapp/src/repos/namespaces/containers/PermissionsNavLink.tsx b/scm-ui/ui-webapp/src/repos/namespaces/containers/PermissionsNavLink.tsx new file mode 100644 index 0000000000..af3094a990 --- /dev/null +++ b/scm-ui/ui-webapp/src/repos/namespaces/containers/PermissionsNavLink.tsx @@ -0,0 +1,47 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +import React from "react"; +import { WithTranslation, withTranslation } from "react-i18next"; +import { Namespace } from "@scm-manager/ui-types"; +import { NavLink } from "@scm-manager/ui-components"; + +type Props = WithTranslation & { + permissionUrl: string; + namespace: Namespace; +}; + +class PermissionsNavLink extends React.Component { + hasPermissionsLink = () => { + return this.props.namespace._links.permissions; + }; + render() { + if (!this.hasPermissionsLink()) { + return null; + } + const { permissionUrl, t } = this.props; + return ; + } +} + +export default withTranslation("namespaces")(PermissionsNavLink); diff --git a/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx b/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx index cb0b285aa4..3ab269db50 100644 --- a/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx +++ b/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx @@ -57,13 +57,12 @@ import { getRepositoryVerbsLink, getUserAutoCompleteLink } from "../../../modules/indexResource"; - type Props = WithTranslation & { availablePermissions: boolean; availableRepositoryRoles: RepositoryRole[]; availableVerbs: string[]; namespace: string; - repoName: string; + repoName?: string; loading: boolean; error: Error; permissions: PermissionCollection; @@ -77,17 +76,17 @@ type Props = WithTranslation & { // dispatch functions fetchAvailablePermissionsIfNeeded: (repositoryRolesLink: string, repositoryVerbsLink: string) => void; - fetchPermissions: (link: string, namespace: string, repoName: string) => void; + fetchPermissions: (link: string, namespace: string, repoName?: string) => void; createPermission: ( link: string, permission: PermissionCreateEntry, namespace: string, - repoName: string, + repoName?: string, callback?: () => void ) => void; - createPermissionReset: (p1: string, p2: string) => void; - modifyPermissionReset: (p1: string, p2: string) => void; - deletePermissionReset: (p1: string, p2: string) => void; + createPermissionReset: (namespace: string, repoName?: string) => void; + modifyPermissionReset: (namespace: string, repoName?: string) => void; + deletePermissionReset: (namespace: string, repoName?: string) => void; // context props match: any; @@ -241,7 +240,7 @@ const mapStateToProps = (state: any, ownProps: Props) => { const mapDispatchToProps = (dispatch: any) => { return { - fetchPermissions: (link: string, namespace: string, repoName: string) => { + fetchPermissions: (link: string, namespace: string, repoName?: string) => { dispatch(fetchPermissions(link, namespace, repoName)); }, fetchAvailablePermissionsIfNeeded: (repositoryRolesLink: string, repositoryVerbsLink: string) => { @@ -256,13 +255,13 @@ const mapDispatchToProps = (dispatch: any) => { ) => { dispatch(createPermission(link, permission, namespace, repoName, callback)); }, - createPermissionReset: (namespace: string, repoName: string) => { + createPermissionReset: (namespace: string, repoName?: string) => { dispatch(createPermissionReset(namespace, repoName)); }, - modifyPermissionReset: (namespace: string, repoName: string) => { + modifyPermissionReset: (namespace: string, repoName?: string) => { dispatch(modifyPermissionReset(namespace, repoName)); }, - deletePermissionReset: (namespace: string, repoName: string) => { + deletePermissionReset: (namespace: string, repoName?: string) => { dispatch(deletePermissionReset(namespace, repoName)); } }; diff --git a/scm-ui/ui-webapp/src/repos/permissions/containers/SinglePermission.tsx b/scm-ui/ui-webapp/src/repos/permissions/containers/SinglePermission.tsx index 3be203f7dc..48337446d4 100644 --- a/scm-ui/ui-webapp/src/repos/permissions/containers/SinglePermission.tsx +++ b/scm-ui/ui-webapp/src/repos/permissions/containers/SinglePermission.tsx @@ -43,14 +43,14 @@ type Props = WithTranslation & { availableRepositoryRoles: RepositoryRole[]; availableRepositoryVerbs: string[]; submitForm: (p: Permission) => void; - modifyPermission: (permission: Permission, namespace: string, name: string) => void; + modifyPermission: (permission: Permission, namespace: string, name?: string) => void; permission: Permission; namespace: string; - repoName: string; + repoName?: string; match: any; history: History; loading: boolean; - deletePermission: (permission: Permission, namespace: string, name: string) => void; + deletePermission: (permission: Permission, namespace: string, name?: string) => void; deleteLoading: boolean; }; diff --git a/scm-ui/ui-webapp/src/repos/permissions/modules/permissions.ts b/scm-ui/ui-webapp/src/repos/permissions/modules/permissions.ts index 55a446ab80..34852c17de 100644 --- a/scm-ui/ui-webapp/src/repos/permissions/modules/permissions.ts +++ b/scm-ui/ui-webapp/src/repos/permissions/modules/permissions.ts @@ -131,7 +131,7 @@ export function fetchAvailableFailure(error: Error): Action { // fetch permissions -export function fetchPermissions(link: string, namespace: string, repoName: string) { +export function fetchPermissions(link: string, namespace: string, repoName?: string) { return function(dispatch: any) { dispatch(fetchPermissionsPending(namespace, repoName)); return apiClient @@ -146,26 +146,26 @@ export function fetchPermissions(link: string, namespace: string, repoName: stri }; } -export function fetchPermissionsPending(namespace: string, repoName: string): Action { +export function fetchPermissionsPending(namespace: string, repoName?: string): Action { return { type: FETCH_PERMISSIONS_PENDING, payload: { namespace, repoName }, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } -export function fetchPermissionsSuccess(permissions: any, namespace: string, repoName: string): Action { +export function fetchPermissionsSuccess(permissions: any, namespace: string, repoName?: string): Action { return { type: FETCH_PERMISSIONS_SUCCESS, payload: permissions, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } -export function fetchPermissionsFailure(namespace: string, repoName: string, error: Error): Action { +export function fetchPermissionsFailure(namespace: string, repoName?: string, error: Error): Action { return { type: FETCH_PERMISSIONS_FAILURE, payload: { @@ -173,13 +173,13 @@ export function fetchPermissionsFailure(namespace: string, repoName: string, err repoName, error }, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } // modify permission -export function modifyPermission(permission: Permission, namespace: string, repoName: string, callback?: () => void) { +export function modifyPermission(permission: Permission, namespace: string, repoName?: string, callback?: () => void) { return function(dispatch: any) { dispatch(modifyPermissionPending(permission, namespace, repoName)); return apiClient @@ -196,7 +196,7 @@ export function modifyPermission(permission: Permission, namespace: string, repo }; } -export function modifyPermissionPending(permission: Permission, namespace: string, repoName: string): Action { +export function modifyPermissionPending(permission: Permission, namespace: string, repoName?: string): Action { return { type: MODIFY_PERMISSION_PENDING, payload: permission, @@ -204,12 +204,12 @@ export function modifyPermissionPending(permission: Permission, namespace: strin }; } -export function modifyPermissionSuccess(permission: Permission, namespace: string, repoName: string): Action { +export function modifyPermissionSuccess(permission: Permission, namespace: string, repoName?: string): Action { return { type: MODIFY_PERMISSION_SUCCESS, payload: { permission, - position: namespace + "/" + repoName + position: createPermissionStateKey(namespace, repoName) }, itemId: createItemId(permission, namespace, repoName) }; @@ -219,7 +219,7 @@ export function modifyPermissionFailure( permission: Permission, error: Error, namespace: string, - repoName: string + repoName?: string ): Action { return { type: MODIFY_PERMISSION_FAILURE, @@ -240,14 +240,14 @@ function newPermissions(oldPermissions: PermissionCollection, newPermission: Per } } -export function modifyPermissionReset(namespace: string, repoName: string) { +export function modifyPermissionReset(namespace: string, repoName?: string) { return { type: MODIFY_PERMISSION_RESET, payload: { namespace, repoName }, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } @@ -256,7 +256,7 @@ export function createPermission( link: string, permission: PermissionCreateEntry, namespace: string, - repoName: string, + repoName?: string, callback?: () => void ) { return function(dispatch: Dispatch) { @@ -281,48 +281,48 @@ export function createPermission( export function createPermissionPending( permission: PermissionCreateEntry, namespace: string, - repoName: string + repoName?: string ): Action { return { type: CREATE_PERMISSION_PENDING, payload: permission, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } export function createPermissionSuccess( permission: PermissionCreateEntry, namespace: string, - repoName: string + repoName?: string ): Action { return { type: CREATE_PERMISSION_SUCCESS, payload: { permission, - position: namespace + "/" + repoName + position: createPermissionStateKey(namespace, repoName) }, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } -export function createPermissionFailure(error: Error, namespace: string, repoName: string): Action { +export function createPermissionFailure(error: Error, namespace: string, repoName?: string): Action { return { type: CREATE_PERMISSION_FAILURE, payload: error, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } -export function createPermissionReset(namespace: string, repoName: string) { +export function createPermissionReset(namespace: string, repoName?: string) { return { type: CREATE_PERMISSION_RESET, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } // delete permission -export function deletePermission(permission: Permission, namespace: string, repoName: string, callback?: () => void) { +export function deletePermission(permission: Permission, namespace: string, repoName?: string, callback?: () => void) { return function(dispatch: any) { dispatch(deletePermissionPending(permission, namespace, repoName)); return apiClient @@ -339,7 +339,7 @@ export function deletePermission(permission: Permission, namespace: string, repo }; } -export function deletePermissionPending(permission: Permission, namespace: string, repoName: string): Action { +export function deletePermissionPending(permission: Permission, namespace: string, repoName?: string): Action { return { type: DELETE_PERMISSION_PENDING, payload: permission, @@ -347,12 +347,12 @@ export function deletePermissionPending(permission: Permission, namespace: strin }; } -export function deletePermissionSuccess(permission: Permission, namespace: string, repoName: string): Action { +export function deletePermissionSuccess(permission: Permission, namespace: string, repoName?: string): Action { return { type: DELETE_PERMISSION_SUCCESS, payload: { permission, - position: namespace + "/" + repoName + position: createPermissionStateKey(namespace, repoName) }, itemId: createItemId(permission, namespace, repoName) }; @@ -361,7 +361,7 @@ export function deletePermissionSuccess(permission: Permission, namespace: strin export function deletePermissionFailure( permission: Permission, namespace: string, - repoName: string, + repoName?: string, error: Error ): Action { return { @@ -374,14 +374,14 @@ export function deletePermissionFailure( }; } -export function deletePermissionReset(namespace: string, repoName: string) { +export function deletePermissionReset(namespace: string, repoName?: string) { return { type: DELETE_PERMISSION_RESET, payload: { namespace, repoName }, - itemId: namespace + "/" + repoName + itemId: createPermissionStateKey(namespace, repoName) }; } @@ -398,9 +398,9 @@ function deletePermissionFromState(oldPermissions: PermissionCollection, permiss return newPermission; } -function createItemId(permission: Permission, namespace: string, repoName: string) { +function createItemId(permission: Permission, namespace: string, repoName?: string) { const groupPermission = permission.groupPermission ? "@" : ""; - return namespace + "/" + repoName + "/" + groupPermission + permission.name; + return createPermissionStateKey(namespace, repoName) + "/" + groupPermission + permission.name; } // reducer @@ -427,7 +427,7 @@ export default function reducer( createPermission: !!action.payload._links.create } }; - case MODIFY_PERMISSION_SUCCESS: + case MODIFY_PERMISSION_SUCCESS: { const positionOfPermission = action.payload.position; const newPermission = newPermissions(state[action.payload.position].entries, action.payload.permission); return { @@ -437,7 +437,8 @@ export default function reducer( entries: newPermission } }; - case CREATE_PERMISSION_SUCCESS: + } + case CREATE_PERMISSION_SUCCESS: { // return state; const position = action.payload.position; const permissions = state[action.payload.position].entries; @@ -449,9 +450,10 @@ export default function reducer( entries: permissions } }; - case DELETE_PERMISSION_SUCCESS: + } + case DELETE_PERMISSION_SUCCESS: { const permissionPosition = action.payload.position; - const new_Permissions = deletePermissionFromState( + const newPermissions = deletePermissionFromState( state[action.payload.position].entries, action.payload.permission ); @@ -459,9 +461,10 @@ export default function reducer( ...state, [permissionPosition]: { ...state[permissionPosition], - entries: new_Permissions + entries: newPermissions } }; + } default: return state; } @@ -490,9 +493,9 @@ function available(state: object) { return {}; } -export function getPermissionsOfRepo(state: object, namespace: string, repoName: string) { - if (state.permissions && state.permissions[namespace + "/" + repoName]) { - return state.permissions[namespace + "/" + repoName].entries; +export function getPermissionsOfRepo(state: object, namespace: string, repoName?: string) { + if (state.permissions && state.permissions[createPermissionStateKey(namespace, repoName)]) { + return state.permissions[createPermissionStateKey(namespace, repoName)].entries; } } @@ -500,52 +503,62 @@ export function isFetchAvailablePermissionsPending(state: object) { return isPending(state, FETCH_AVAILABLE, "available"); } -export function isFetchPermissionsPending(state: object, namespace: string, repoName: string) { - return isPending(state, FETCH_PERMISSIONS, namespace + "/" + repoName); +export function isFetchPermissionsPending(state: object, namespace: string, repoName?: string) { + return isPending(state, FETCH_PERMISSIONS, createPermissionStateKey(namespace, repoName)); } export function getFetchAvailablePermissionsFailure(state: object) { return getFailure(state, FETCH_AVAILABLE, "available"); } -export function getFetchPermissionsFailure(state: object, namespace: string, repoName: string) { - return getFailure(state, FETCH_PERMISSIONS, namespace + "/" + repoName); +export function getFetchPermissionsFailure(state: object, namespace: string, repoName?: string) { + return getFailure(state, FETCH_PERMISSIONS, createPermissionStateKey(namespace, repoName)); } -export function isModifyPermissionPending(state: object, namespace: string, repoName: string, permission: Permission) { - return isPending(state, MODIFY_PERMISSION, createItemId(permission, namespace, repoName)); +export function isModifyPermissionPending(state: object, namespace: string, repoName?: string, permission: Permission) { + return isPending(state, MODIFY_PERMISSION, createItemId(permission, createPermissionStateKey(namespace, repoName))); } -export function getModifyPermissionFailure(state: object, namespace: string, repoName: string, permission: Permission) { - return getFailure(state, MODIFY_PERMISSION, createItemId(permission, namespace, repoName)); +export function getModifyPermissionFailure( + state: object, + namespace: string, + repoName?: string, + permission: Permission +) { + return getFailure(state, MODIFY_PERMISSION, createItemId(permission, createPermissionStateKey(namespace, repoName))); } -export function hasCreatePermission(state: object, namespace: string, repoName: string) { - if (state.permissions && state.permissions[namespace + "/" + repoName]) - return state.permissions[namespace + "/" + repoName].createPermission; +export function hasCreatePermission(state: object, namespace: string, repoName?: string) { + if (state.permissions && state.permissions[createPermissionStateKey(namespace, repoName)]) + return state.permissions[createPermissionStateKey(namespace, repoName)].createPermission; else return null; } -export function isCreatePermissionPending(state: object, namespace: string, repoName: string) { - return isPending(state, CREATE_PERMISSION, namespace + "/" + repoName); +export function isCreatePermissionPending(state: object, namespace: string, repoName?: string) { + return isPending(state, CREATE_PERMISSION, createPermissionStateKey(namespace, repoName)); } -export function getCreatePermissionFailure(state: object, namespace: string, repoName: string) { - return getFailure(state, CREATE_PERMISSION, namespace + "/" + repoName); +export function getCreatePermissionFailure(state: object, namespace: string, repoName?: string) { + return getFailure(state, CREATE_PERMISSION, createPermissionStateKey(namespace, repoName)); } -export function isDeletePermissionPending(state: object, namespace: string, repoName: string, permission: Permission) { +export function isDeletePermissionPending(state: object, namespace: string, repoName?: string, permission: Permission) { return isPending(state, DELETE_PERMISSION, createItemId(permission, namespace, repoName)); } -export function getDeletePermissionFailure(state: object, namespace: string, repoName: string, permission: Permission) { +export function getDeletePermissionFailure( + state: object, + namespace: string, + repoName?: string, + permission: Permission +) { return getFailure(state, DELETE_PERMISSION, createItemId(permission, namespace, repoName)); } -export function getDeletePermissionsFailure(state: object, namespace: string, repoName: string) { +export function getDeletePermissionsFailure(state: object, namespace: string, repoName?: string) { const permissions = - state.permissions && state.permissions[namespace + "/" + repoName] - ? state.permissions[namespace + "/" + repoName].entries + state.permissions && state.permissions[createPermissionStateKey(namespace, repoName)] + ? state.permissions[createPermissionStateKey(namespace, repoName)].entries : null; if (permissions == null) return undefined; for (let i = 0; i < permissions.length; i++) { @@ -556,10 +569,10 @@ export function getDeletePermissionsFailure(state: object, namespace: string, re return null; } -export function getModifyPermissionsFailure(state: object, namespace: string, repoName: string) { +export function getModifyPermissionsFailure(state: object, namespace: string, repoName?: string) { const permissions = - state.permissions && state.permissions[namespace + "/" + repoName] - ? state.permissions[namespace + "/" + repoName].entries + state.permissions && state.permissions[createPermissionStateKey(namespace, repoName)] + ? state.permissions[createPermissionStateKey(namespace, repoName)].entries : null; if (permissions == null) return undefined; for (let i = 0; i < permissions.length; i++) { @@ -570,6 +583,10 @@ export function getModifyPermissionsFailure(state: object, namespace: string, re return null; } +function createPermissionStateKey(namespace: string, repoName?: string) { + return namespace + (repoName ? "/" + repoName : ""); +} + export function findVerbsForRole(availableRepositoryRoles: RepositoryRole[], roleName: string) { const matchingRole = availableRepositoryRoles.find(role => roleName === role.name); if (matchingRole) {