From 5d3cbff461c475283e24bb46af0a288749f062b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 24 Jan 2019 08:05:28 +0100 Subject: [PATCH] Load available repository permissions --- .../src/AvailableRepositoryPermissions.js | 11 + .../ui-types/src/RepositoryPermissions.js | 2 +- .../packages/ui-types/src/index.js | 2 + .../permissions/containers/Permissions.js | 462 +++++++++--------- .../repos/permissions/modules/permissions.js | 95 +++- 5 files changed, 344 insertions(+), 228 deletions(-) create mode 100644 scm-ui-components/packages/ui-types/src/AvailableRepositoryPermissions.js diff --git a/scm-ui-components/packages/ui-types/src/AvailableRepositoryPermissions.js b/scm-ui-components/packages/ui-types/src/AvailableRepositoryPermissions.js new file mode 100644 index 0000000000..ab7e8d82e4 --- /dev/null +++ b/scm-ui-components/packages/ui-types/src/AvailableRepositoryPermissions.js @@ -0,0 +1,11 @@ +// @flow + +export type RepositoryRole = { + name: string, + verbs: string[] +}; + +export type AvailableRepositoryPermissions = { + availableVerbs: string[], + availableRoles: RepositoryRole[] +}; diff --git a/scm-ui-components/packages/ui-types/src/RepositoryPermissions.js b/scm-ui-components/packages/ui-types/src/RepositoryPermissions.js index 4352c21da6..ed3c925283 100644 --- a/scm-ui-components/packages/ui-types/src/RepositoryPermissions.js +++ b/scm-ui-components/packages/ui-types/src/RepositoryPermissions.js @@ -7,7 +7,7 @@ export type Permission = PermissionCreateEntry & { export type PermissionCreateEntry = { name: string, - type: string, + verbs: string[], groupPermission: boolean } diff --git a/scm-ui-components/packages/ui-types/src/index.js b/scm-ui-components/packages/ui-types/src/index.js index cf739f747d..f7b375ac98 100644 --- a/scm-ui-components/packages/ui-types/src/index.js +++ b/scm-ui-components/packages/ui-types/src/index.js @@ -24,3 +24,5 @@ export type { Permission, PermissionCreateEntry, PermissionCollection } from "./ export type { SubRepository, File } from "./Sources"; export type { SelectValue, AutocompleteObject } from "./Autocomplete"; + +export type { AvailableRepositoryPermissions, RepositoryRole } from "./AvailableRepositoryPermissions"; diff --git a/scm-ui/src/repos/permissions/containers/Permissions.js b/scm-ui/src/repos/permissions/containers/Permissions.js index 48f4e585f7..4d17f65e16 100644 --- a/scm-ui/src/repos/permissions/containers/Permissions.js +++ b/scm-ui/src/repos/permissions/containers/Permissions.js @@ -1,225 +1,237 @@ -//@flow -import React from "react"; -import { connect } from "react-redux"; -import { translate } from "react-i18next"; -import { - fetchPermissions, - getFetchPermissionsFailure, - isFetchPermissionsPending, - getPermissionsOfRepo, - hasCreatePermission, - createPermission, - isCreatePermissionPending, - getCreatePermissionFailure, - createPermissionReset, - getDeletePermissionsFailure, - getModifyPermissionsFailure, - modifyPermissionReset, - deletePermissionReset -} from "../modules/permissions"; -import { Loading, ErrorPage } from "@scm-manager/ui-components"; -import type { - Permission, - PermissionCollection, - PermissionCreateEntry -} from "@scm-manager/ui-types"; -import SinglePermission from "./SinglePermission"; -import CreatePermissionForm from "../components/CreatePermissionForm"; -import type { History } from "history"; -import { getPermissionsLink } from "../../modules/repos"; -import { - getGroupAutoCompleteLink, - getUserAutoCompleteLink -} from "../../../modules/indexResource"; - -type Props = { - namespace: string, - repoName: string, - loading: boolean, - error: Error, - permissions: PermissionCollection, - hasPermissionToCreate: boolean, - loadingCreatePermission: boolean, - permissionsLink: string, - groupAutoCompleteLink: string, - userAutoCompleteLink: string, - - //dispatch functions - fetchPermissions: (link: string, namespace: string, repoName: string) => void, - createPermission: ( - link: string, - permission: PermissionCreateEntry, - namespace: string, - repoName: string, - callback?: () => void - ) => void, - createPermissionReset: (string, string) => void, - modifyPermissionReset: (string, string) => void, - deletePermissionReset: (string, string) => void, - // context props - t: string => string, - match: any, - history: History -}; - -class Permissions extends React.Component { - componentDidMount() { - const { - fetchPermissions, - namespace, - repoName, - modifyPermissionReset, - createPermissionReset, - deletePermissionReset, - permissionsLink - } = this.props; - - createPermissionReset(namespace, repoName); - modifyPermissionReset(namespace, repoName); - deletePermissionReset(namespace, repoName); - fetchPermissions(permissionsLink, namespace, repoName); - } - - createPermission = (permission: Permission) => { - this.props.createPermission( - this.props.permissionsLink, - permission, - this.props.namespace, - this.props.repoName - ); - }; - - render() { - const { - loading, - error, - permissions, - t, - namespace, - repoName, - loadingCreatePermission, - hasPermissionToCreate, - userAutoCompleteLink, - groupAutoCompleteLink - } = this.props; - if (error) { - return ( - - ); - } - - if (loading || !permissions) { - return ; - } - - const createPermissionForm = hasPermissionToCreate ? ( - this.createPermission(permission)} - loading={loadingCreatePermission} - currentPermissions={permissions} - userAutoCompleteLink={userAutoCompleteLink} - groupAutoCompleteLink={groupAutoCompleteLink} - /> - ) : null; - - return ( -
- - - - - - - - - - {permissions.map(permission => { - return ( - - ); - })} - -
{t("permission.name")} - {t("permission.group-permission")} - {t("permission.type")} -
- {createPermissionForm} -
- ); - } -} - -const mapStateToProps = (state, ownProps) => { - const namespace = ownProps.namespace; - const repoName = ownProps.repoName; - const error = - getFetchPermissionsFailure(state, namespace, repoName) || - getCreatePermissionFailure(state, namespace, repoName) || - getDeletePermissionsFailure(state, namespace, repoName) || - getModifyPermissionsFailure(state, namespace, repoName); - const loading = isFetchPermissionsPending(state, namespace, repoName); - const permissions = getPermissionsOfRepo(state, namespace, repoName); - const loadingCreatePermission = isCreatePermissionPending( - state, - namespace, - repoName - ); - const hasPermissionToCreate = hasCreatePermission(state, namespace, repoName); - const permissionsLink = getPermissionsLink(state, namespace, repoName); - const groupAutoCompleteLink = getGroupAutoCompleteLink(state); - const userAutoCompleteLink = getUserAutoCompleteLink(state); - return { - namespace, - repoName, - error, - loading, - permissions, - hasPermissionToCreate, - loadingCreatePermission, - permissionsLink, - groupAutoCompleteLink, - userAutoCompleteLink - }; -}; - -const mapDispatchToProps = dispatch => { - return { - fetchPermissions: (link: string, namespace: string, repoName: string) => { - dispatch(fetchPermissions(link, namespace, repoName)); - }, - createPermission: ( - link: string, - permission: PermissionCreateEntry, - namespace: string, - repoName: string, - callback?: () => void - ) => { - dispatch( - createPermission(link, permission, namespace, repoName, callback) - ); - }, - createPermissionReset: (namespace: string, repoName: string) => { - dispatch(createPermissionReset(namespace, repoName)); - }, - modifyPermissionReset: (namespace: string, repoName: string) => { - dispatch(modifyPermissionReset(namespace, repoName)); - }, - deletePermissionReset: (namespace: string, repoName: string) => { - dispatch(deletePermissionReset(namespace, repoName)); - } - }; -}; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(translate("repos")(Permissions)); +//@flow +import React from "react"; +import { connect } from "react-redux"; +import { translate } from "react-i18next"; +import { + fetchAvailablePermissionsIfNeeded, + fetchPermissions, + getFetchAvailablePermissionsFailure, + getFetchPermissionsFailure, + isFetchAvailablePermissionsPending, + isFetchPermissionsPending, + getPermissionsOfRepo, + hasCreatePermission, + createPermission, + isCreatePermissionPending, + getCreatePermissionFailure, + createPermissionReset, + getDeletePermissionsFailure, + getModifyPermissionsFailure, + modifyPermissionReset, + deletePermissionReset +} from "../modules/permissions"; +import { Loading, ErrorPage } from "@scm-manager/ui-components"; +import type { + Permission, + PermissionCollection, + PermissionCreateEntry +} from "@scm-manager/ui-types"; +import SinglePermission from "./SinglePermission"; +import CreatePermissionForm from "../components/CreatePermissionForm"; +import type { History } from "history"; +import { getPermissionsLink } from "../../modules/repos"; +import { + getGroupAutoCompleteLink, + getUserAutoCompleteLink +} from "../../../modules/indexResource"; + +type Props = { + namespace: string, + repoName: string, + loading: boolean, + error: Error, + permissions: PermissionCollection, + hasPermissionToCreate: boolean, + loadingCreatePermission: boolean, + permissionsLink: string, + groupAutoCompleteLink: string, + userAutoCompleteLink: string, + + //dispatch functions + fetchAvailablePermissionsIfNeeded: () => void, + fetchPermissions: (link: string, namespace: string, repoName: string) => void, + createPermission: ( + link: string, + permission: PermissionCreateEntry, + namespace: string, + repoName: string, + callback?: () => void + ) => void, + createPermissionReset: (string, string) => void, + modifyPermissionReset: (string, string) => void, + deletePermissionReset: (string, string) => void, + // context props + t: string => string, + match: any, + history: History +}; + +class Permissions extends React.Component { + componentDidMount() { + const { + fetchAvailablePermissionsIfNeeded, + fetchPermissions, + namespace, + repoName, + modifyPermissionReset, + createPermissionReset, + deletePermissionReset, + permissionsLink + } = this.props; + + createPermissionReset(namespace, repoName); + modifyPermissionReset(namespace, repoName); + deletePermissionReset(namespace, repoName); + fetchAvailablePermissionsIfNeeded(); + fetchPermissions(permissionsLink, namespace, repoName); + } + + createPermission = (permission: Permission) => { + this.props.createPermission( + this.props.permissionsLink, + permission, + this.props.namespace, + this.props.repoName + ); + }; + + render() { + const { + loading, + error, + permissions, + t, + namespace, + repoName, + loadingCreatePermission, + hasPermissionToCreate, + userAutoCompleteLink, + groupAutoCompleteLink + } = this.props; + if (error) { + return ( + + ); + } + + if (loading || !permissions) { + return ; + } + + const createPermissionForm = hasPermissionToCreate ? ( + this.createPermission(permission)} + loading={loadingCreatePermission} + currentPermissions={permissions} + userAutoCompleteLink={userAutoCompleteLink} + groupAutoCompleteLink={groupAutoCompleteLink} + /> + ) : null; + + return ( +
+ + + + + + + + + + {permissions.map(permission => { + return ( + + ); + })} + +
{t("permission.name")} + {t("permission.group-permission")} + {t("permission.type")} +
+ {createPermissionForm} +
+ ); + } +} + +const mapStateToProps = (state, ownProps) => { + const namespace = ownProps.namespace; + const repoName = ownProps.repoName; + const error = + getFetchPermissionsFailure(state, namespace, repoName) || + getCreatePermissionFailure(state, namespace, repoName) || + getDeletePermissionsFailure(state, namespace, repoName) || + getModifyPermissionsFailure(state, namespace, repoName) || + getFetchAvailablePermissionsFailure(state); + const loading = + isFetchPermissionsPending(state, namespace, repoName) || + isFetchAvailablePermissionsPending(state); + const permissions = getPermissionsOfRepo(state, namespace, repoName); + const loadingCreatePermission = isCreatePermissionPending( + state, + namespace, + repoName + ); + const hasPermissionToCreate = hasCreatePermission(state, namespace, repoName); + const permissionsLink = getPermissionsLink(state, namespace, repoName); + const groupAutoCompleteLink = getGroupAutoCompleteLink(state); + const userAutoCompleteLink = getUserAutoCompleteLink(state); + return { + namespace, + repoName, + error, + loading, + permissions, + hasPermissionToCreate, + loadingCreatePermission, + permissionsLink, + groupAutoCompleteLink, + userAutoCompleteLink + }; +}; + +const mapDispatchToProps = dispatch => { + return { + fetchPermissions: (link: string, namespace: string, repoName: string) => { + dispatch(fetchPermissions(link, namespace, repoName)); + }, + fetchAvailablePermissionsIfNeeded: () => { + dispatch(fetchAvailablePermissionsIfNeeded()); + }, + createPermission: ( + link: string, + permission: PermissionCreateEntry, + namespace: string, + repoName: string, + callback?: () => void + ) => { + dispatch( + createPermission(link, permission, namespace, repoName, callback) + ); + }, + createPermissionReset: (namespace: string, repoName: string) => { + dispatch(createPermissionReset(namespace, repoName)); + }, + modifyPermissionReset: (namespace: string, repoName: string) => { + dispatch(modifyPermissionReset(namespace, repoName)); + }, + deletePermissionReset: (namespace: string, repoName: string) => { + dispatch(deletePermissionReset(namespace, repoName)); + } + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(translate("repos")(Permissions)); diff --git a/scm-ui/src/repos/permissions/modules/permissions.js b/scm-ui/src/repos/permissions/modules/permissions.js index 9f5330bbcd..74d0ea5bc7 100644 --- a/scm-ui/src/repos/permissions/modules/permissions.js +++ b/scm-ui/src/repos/permissions/modules/permissions.js @@ -4,6 +4,7 @@ import type { Action } from "@scm-manager/ui-components"; import { apiClient } from "@scm-manager/ui-components"; import * as types from "../../../modules/types"; import type { + AvailableRepositoryPermissions, Permission, PermissionCollection, PermissionCreateEntry @@ -11,7 +12,18 @@ import type { import { isPending } from "../../../modules/pending"; import { getFailure } from "../../../modules/failure"; import { Dispatch } from "redux"; +import { getLinks } from "../../../modules/indexResource"; +export const FETCH_AVAILABLE = "scm/permissions/FETCH_AVAILABLE"; +export const FETCH_AVAILABLE_PENDING = `${FETCH_AVAILABLE}_${ + types.PENDING_SUFFIX +}`; +export const FETCH_AVAILABLE_SUCCESS = `${FETCH_AVAILABLE}_${ + types.SUCCESS_SUFFIX +}`; +export const FETCH_AVAILABLE_FAILURE = `${FETCH_AVAILABLE}_${ + types.FAILURE_SUFFIX +}`; export const FETCH_PERMISSIONS = "scm/permissions/FETCH_PERMISSIONS"; export const FETCH_PERMISSIONS_PENDING = `${FETCH_PERMISSIONS}_${ types.PENDING_SUFFIX @@ -62,7 +74,71 @@ export const DELETE_PERMISSION_RESET = `${DELETE_PERMISSION}_${ types.RESET_SUFFIX }`; -const CONTENT_TYPE = "application/vnd.scmm-permission+json"; +const CONTENT_TYPE = "application/vnd.scmm-repositoryPermission+json"; + +// fetch available permissions + +export function fetchAvailablePermissionsIfNeeded() { + return function(dispatch: any, getState: () => Object) { + if (shouldFetchAvailablePermissions(getState())) { + return fetchAvailablePermissions(dispatch, getState); + } + }; +} + +export function fetchAvailablePermissions( + dispatch: any, + getState: () => Object +) { + dispatch(fetchAvailablePending()); + return apiClient + .get(getLinks(getState()).availableRepositoryPermissions.href) + .then(response => response.json()) + .then(available => { + dispatch(fetchAvailableSuccess(available)); + }) + .catch(err => { + dispatch(fetchAvailableFailure(err)); + }); +} + +export function shouldFetchAvailablePermissions(state: Object) { + if ( + isFetchAvailablePermissionsPending(state) || + getFetchAvailablePermissionsFailure(state) + ) { + return false; + } + return !state.available; +} + +export function fetchAvailablePending(): Action { + return { + type: FETCH_AVAILABLE_PENDING, + payload: {}, + itemId: "available" + }; +} + +export function fetchAvailableSuccess( + available: AvailableRepositoryPermissions +): Action { + return { + type: FETCH_AVAILABLE_SUCCESS, + payload: available, + itemId: "available" + }; +} + +export function fetchAvailableFailure(error: Error): Action { + return { + type: FETCH_AVAILABLE_FAILURE, + payload: { + error + }, + itemId: "available" + }; +} // fetch permissions @@ -368,6 +444,7 @@ export function deletePermissionReset(namespace: string, repoName: string) { itemId: namespace + "/" + repoName }; } + function deletePermissionFromState( oldPermissions: PermissionCollection, permission: Permission @@ -399,12 +476,17 @@ export default function reducer( return state; } switch (action.type) { + case FETCH_AVAILABLE_SUCCESS: + return { + ...state, + available: action.payload + }; case FETCH_PERMISSIONS_SUCCESS: return { ...state, [action.itemId]: { entries: action.payload._embedded.permissions, - createPermission: action.payload._links.create ? true : false + createPermission: !!action.payload._links.create } }; case MODIFY_PERMISSION_SUCCESS: @@ -463,6 +545,10 @@ export function getPermissionsOfRepo( } } +export function isFetchAvailablePermissionsPending(state: Object) { + return isPending(state, FETCH_AVAILABLE, "available"); +} + export function isFetchPermissionsPending( state: Object, namespace: string, @@ -471,6 +557,10 @@ export function isFetchPermissionsPending( return isPending(state, FETCH_PERMISSIONS, namespace + "/" + repoName); } +export function getFetchAvailablePermissionsFailure(state: Object) { + return getFailure(state, FETCH_AVAILABLE, "available"); +} + export function getFetchPermissionsFailure( state: Object, namespace: string, @@ -522,6 +612,7 @@ export function isCreatePermissionPending( ) { return isPending(state, CREATE_PERMISSION, namespace + "/" + repoName); } + export function getCreatePermissionFailure( state: Object, namespace: string,