X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/2a1d30b31a3d93d94ea0651dc7c8944d83a11e9e..e5e090c801a015f04c04327a176faf54ba3650b7:/src/store/sharing-dialog/sharing-dialog-actions.ts diff --git a/src/store/sharing-dialog/sharing-dialog-actions.ts b/src/store/sharing-dialog/sharing-dialog-actions.ts index efd53295..37de6f8c 100644 --- a/src/store/sharing-dialog/sharing-dialog-actions.ts +++ b/src/store/sharing-dialog/sharing-dialog-actions.ts @@ -4,13 +4,210 @@ import { dialogActions } from "~/store/dialog/dialog-actions"; import { withDialog } from "~/store/dialog/with-dialog"; - -export const SHARING_DIALOG_NAME = 'SHARING_DIALOG_NAME'; +import { SHARING_DIALOG_NAME, SharingPublicAccessFormData, SHARING_PUBLIC_ACCESS_FORM_NAME, SHARING_INVITATION_FORM_NAME, SharingManagementFormData, SharingInvitationFormData, VisibilityLevel, getSharingMangementFormData, getSharingPublicAccessFormData } from './sharing-dialog-types'; +import { Dispatch } from 'redux'; +import { ServiceRepository } from "~/services/services"; +import { FilterBuilder } from '~/services/api/filter-builder'; +import { initialize, getFormValues, reset } from 'redux-form'; +import { SHARING_MANAGEMENT_FORM_NAME } from '~/store/sharing-dialog/sharing-dialog-types'; +import { RootState } from '~/store/store'; +import { getDialog } from '~/store/dialog/dialog-reducer'; +import { PermissionLevel } from '~/models/permission'; +import { getPublicGroupUuid } from "~/store/workflow-panel/workflow-panel-actions"; +import { PermissionResource } from '~/models/permission'; +import { differenceWith } from "lodash"; +import { withProgress } from "~/store/progress-indicator/with-progress"; +import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions.ts'; +import { snackbarActions, SnackbarKind } from "../snackbar/snackbar-actions"; export const openSharingDialog = (resourceUuid: string) => - dialogActions.OPEN_DIALOG({ id: SHARING_DIALOG_NAME, data: resourceUuid }); + (dispatch: Dispatch) => { + dispatch(dialogActions.OPEN_DIALOG({ id: SHARING_DIALOG_NAME, data: resourceUuid })); + dispatch(loadSharingDialog); + }; export const closeSharingDialog = () => dialogActions.CLOSE_DIALOG({ id: SHARING_DIALOG_NAME }); export const connectSharingDialog = withDialog(SHARING_DIALOG_NAME); +export const connectSharingDialogProgress = withProgress(SHARING_DIALOG_NAME); + + +export const saveSharingDialogChanges = async (dispatch: Dispatch) => { + dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME)); + await dispatch(savePublicPermissionChanges); + await dispatch(saveManagementChanges); + await dispatch(sendInvitations); + dispatch(reset(SHARING_INVITATION_FORM_NAME)); + await dispatch(loadSharingDialog); +}; + +export const sendSharingInvitations = async (dispatch: Dispatch) => { + dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME)); + await dispatch(sendInvitations); + dispatch(closeSharingDialog()); + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Resource has been shared', + kind: SnackbarKind.SUCCESS, + })); + dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); +}; + +const loadSharingDialog = async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { + + const dialog = getDialog(getState().dialog, SHARING_DIALOG_NAME); + if (dialog) { + dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME)); + try { + const { items } = await permissionService.listResourcePermissions(dialog.data); + dispatch(initializePublicAccessForm(items)); + await dispatch(initializeManagementForm(items)); + dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); + } catch (e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'You do not have access to share this item', hideDuration: 2000, kind: SnackbarKind.ERROR })); + dispatch(dialogActions.CLOSE_DIALOG({ id: SHARING_DIALOG_NAME })); + dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); + } + } +}; + +const initializeManagementForm = (permissionLinks: PermissionResource[]) => + async (dispatch: Dispatch, getState: () => RootState, { userService }: ServiceRepository) => { + + const filters = new FilterBuilder() + .addIn('uuid', permissionLinks.map(({ tailUuid }) => tailUuid)) + .getFilters(); + + const { items: users } = await userService.list({ filters }); + + const getEmail = (tailUuid: string) => { + const user = users.find(({ uuid }) => uuid === tailUuid); + return user + ? user.email + : tailUuid; + }; + + const managementPermissions = permissionLinks + .filter(item => + item.tailUuid !== getPublicGroupUuid(getState())) + .map(({ tailUuid, name, uuid }) => ({ + email: getEmail(tailUuid), + permissions: name as PermissionLevel, + permissionUuid: uuid, + })); + + const managementFormData: SharingManagementFormData = { + permissions: managementPermissions, + initialPermissions: managementPermissions, + }; + + dispatch(initialize(SHARING_MANAGEMENT_FORM_NAME, managementFormData)); + }; + +const initializePublicAccessForm = (permissionLinks: PermissionResource[]) => + (dispatch: Dispatch, getState: () => RootState, ) => { + + const [publicPermission] = permissionLinks + .filter(item => item.tailUuid === getPublicGroupUuid(getState())); + + const publicAccessFormData: SharingPublicAccessFormData = publicPermission + ? { + visibility: VisibilityLevel.PUBLIC, + permissionUuid: publicPermission.uuid, + } + : { + visibility: permissionLinks.length > 0 + ? VisibilityLevel.SHARED + : VisibilityLevel.PRIVATE, + permissionUuid: '', + }; + + dispatch(initialize(SHARING_PUBLIC_ACCESS_FORM_NAME, publicAccessFormData)); + }; + +const savePublicPermissionChanges = async (_: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { + const state = getState(); + const { user } = state.auth; + const dialog = getDialog(state.dialog, SHARING_DIALOG_NAME); + if (dialog && user) { + const { permissionUuid, visibility } = getSharingPublicAccessFormData(state); + + if (permissionUuid) { + if (visibility === VisibilityLevel.PUBLIC) { + await permissionService.update(permissionUuid, { + name: PermissionLevel.CAN_READ + }); + } else { + await permissionService.delete(permissionUuid); + } + + } else if (visibility === VisibilityLevel.PUBLIC) { + + await permissionService.create({ + ownerUuid: user.uuid, + headUuid: dialog.data, + tailUuid: getPublicGroupUuid(state), + name: PermissionLevel.CAN_READ, + }); + } + } +}; + +const saveManagementChanges = async (_: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { + const state = getState(); + const { user } = state.auth; + const dialog = getDialog(state.dialog, SHARING_DIALOG_NAME); + if (dialog && user) { + + const { initialPermissions, permissions } = getSharingMangementFormData(state); + const { visibility } = getSharingPublicAccessFormData(state); + + + if (visibility === VisibilityLevel.PRIVATE) { + + for (const permission of initialPermissions) { + await permissionService.delete(permission.permissionUuid); + } + + } else { + + const cancelledPermissions = differenceWith( + initialPermissions, + permissions, + (a, b) => a.permissionUuid === b.permissionUuid + ); + + for (const { permissionUuid } of cancelledPermissions) { + await permissionService.delete(permissionUuid); + } + + for (const permission of permissions) { + await permissionService.update(permission.permissionUuid, { name: permission.permissions }); + } + + } + } +}; + +const sendInvitations = async (_: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { + const state = getState(); + const { user } = state.auth; + const dialog = getDialog(state.dialog, SHARING_DIALOG_NAME); + if (dialog && user) { + + const invitations = getFormValues(SHARING_INVITATION_FORM_NAME)(state) as SharingInvitationFormData; + + const invitationData = invitations.invitedPeople + .map(person => ({ + ownerUuid: user.uuid, + headUuid: dialog.data, + tailUuid: person.uuid, + name: invitations.permissions + })); + + for (const invitation of invitationData) { + await permissionService.create(invitation); + } + + } +};