From c6e1fef352129f214501b70ab70611fb05660e91 Mon Sep 17 00:00:00 2001 From: Michal Klobukowski Date: Tue, 30 Oct 2018 09:24:31 +0100 Subject: [PATCH] Implement sharing save operation Feature #14365 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- .../sharing-dialog/sharing-dialog-actions.ts | 169 ++++++++++++------ .../sharing-dialog/sharing-dialog-types.ts | 3 + 2 files changed, 120 insertions(+), 52 deletions(-) diff --git a/src/store/sharing-dialog/sharing-dialog-actions.ts b/src/store/sharing-dialog/sharing-dialog-actions.ts index 76d54e17..f118c70f 100644 --- a/src/store/sharing-dialog/sharing-dialog-actions.ts +++ b/src/store/sharing-dialog/sharing-dialog-actions.ts @@ -12,86 +12,156 @@ import { initialize, getFormValues, isDirty, 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 { PermissionLevel } from '~/models/permission'; import { getPublicGroupUuid } from "~/store/workflow-panel/workflow-panel-actions"; +import { PermissionResource } from '~/models/permission'; +import { differenceWith } from "lodash"; export const openSharingDialog = (resourceUuid: string) => - async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { - + (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 saveSharingDialogChanges = async (dispatch: Dispatch) => { + await Promise.all([ + dispatch(savePublicPermissionChanges), + dispatch(saveManagementChanges), + dispatch(sendInvitations), + ]); + dispatch(reset(SHARING_INVITATION_FORM_NAME)); + await dispatch(loadSharingDialog); +}; + +export const hasChanges = (state: RootState) => + isDirty(SHARING_PUBLIC_ACCESS_FORM_NAME)(state) || + isDirty(SHARING_MANAGEMENT_FORM_NAME)(state) || + isDirty(SHARING_INVITATION_FORM_NAME)(state); + +const loadSharingDialog = async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { - const state = getState(); - const { items } = await permissionService.listResourcePermissions(resourceUuid); + const dialog = getDialog(getState().dialog, SHARING_DIALOG_NAME); + + if (dialog) { + const { items } = await permissionService.listResourcePermissions(dialog.data); + dispatch(initializePublicAccessForm(items)); + await dispatch(initializeManagementForm(items)); + } +}; + +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: items - .filter(item => - item.tailUuid !== getPublicGroupUuid(state)) - .map(({ tailUuid, name }) => ({ - email: tailUuid, - permissions: name as PermissionLevel, - })) + permissions: managementPermissions, + initialPermissions: managementPermissions, }; dispatch(initialize(SHARING_MANAGEMENT_FORM_NAME, managementFormData)); + }; - const [publicPermission] = items.filter(item => item.tailUuid === getPublicGroupUuid(state)); - if (publicPermission) { - const publicAccessFormData: SharingPublicAccessFormData = { +const initializePublicAccessForm = (permissionLinks: PermissionResource[]) => + (dispatch: Dispatch, getState: () => RootState, ) => { + + const [publicPermission] = permissionLinks + .filter(item => item.tailUuid === getPublicGroupUuid(getState())); + + const publicAccessFormData: SharingPublicAccessFormData = publicPermission + ? { enabled: publicPermission.name !== PermissionLevel.NONE, permissions: publicPermission.name as PermissionLevel, + permissionUuid: publicPermission.uuid, + } + : { + enabled: false, + permissions: PermissionLevel.CAN_READ, + permissionUuid: '', }; - dispatch(initialize(SHARING_PUBLIC_ACCESS_FORM_NAME, publicAccessFormData)); - } else { - dispatch(reset(SHARING_PUBLIC_ACCESS_FORM_NAME)); - } + dispatch(initialize(SHARING_PUBLIC_ACCESS_FORM_NAME, publicAccessFormData)); }; -export const closeSharingDialog = () => - dialogActions.CLOSE_DIALOG({ id: SHARING_DIALOG_NAME }); - -export const connectSharingDialog = withDialog(SHARING_DIALOG_NAME); - -export const saveSharingDialogChanges = async (dispatch: Dispatch) => { - dispatch(savePublicPermissionChanges); - dispatch(sendInvitations); -}; - -const savePublicPermissionChanges = async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { +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 publicAccess = getFormValues(SHARING_PUBLIC_ACCESS_FORM_NAME)(state) as SharingPublicAccessFormData; - - const filters = new FilterBuilder() - .addEqual('headUuid', dialog.data) - .getFilters(); - - const { items } = await permissionService.list({ filters }); - - const [publicPermission] = items.filter(item => item.tailUuid === getPublicGroupUuid(state)); + const { permissionUuid, enabled, permissions } = getFormValues(SHARING_PUBLIC_ACCESS_FORM_NAME)(state) as SharingPublicAccessFormData; - if (publicPermission) { + if (permissionUuid) { + if (enabled) { + await permissionService.update(permissionUuid, { + name: enabled ? permissions : PermissionLevel.NONE + }); + } else { + await permissionService.delete(permissionUuid); + } - await permissionService.update(publicPermission.uuid, { - name: publicAccess.enabled ? publicAccess.permissions : PermissionLevel.NONE - }); - - } else { + } else if (enabled) { await permissionService.create({ ownerUuid: user.uuid, headUuid: dialog.data, tailUuid: getPublicGroupUuid(state), - name: publicAccess.enabled ? publicAccess.permissions : PermissionLevel.NONE + name: permissions, }); } } }; -const sendInvitations = async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { +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 } = getFormValues(SHARING_MANAGEMENT_FORM_NAME)(state) as SharingManagementFormData; + + const cancelledPermissions = differenceWith( + initialPermissions, + permissions, + (a, b) => a.permissionUuid === b.permissionUuid + ); + + await Promise.all(cancelledPermissions.map(({ permissionUuid }) => + permissionService.delete(permissionUuid) + )); + + await Promise.all(permissions.map(({ permissionUuid, permissions }) => + permissionService.update(permissionUuid, { name: permissions }) + )); + + } +}; + +const sendInvitations = async (_: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { const state = getState(); const { user } = state.auth; const dialog = getDialog(state.dialog, SHARING_DIALOG_NAME); @@ -111,8 +181,3 @@ const sendInvitations = async (dispatch: Dispatch, getState: () => RootState, { await Promise.all(promises); } }; - -export const hasChanges = (state: RootState) => - isDirty(SHARING_PUBLIC_ACCESS_FORM_NAME)(state) || - isDirty(SHARING_MANAGEMENT_FORM_NAME)(state) || - isDirty(SHARING_INVITATION_FORM_NAME)(state); diff --git a/src/store/sharing-dialog/sharing-dialog-types.ts b/src/store/sharing-dialog/sharing-dialog-types.ts index 634e9758..ad023961 100644 --- a/src/store/sharing-dialog/sharing-dialog-types.ts +++ b/src/store/sharing-dialog/sharing-dialog-types.ts @@ -12,15 +12,18 @@ export const SHARING_INVITATION_FORM_NAME = 'SHARING_INVITATION_FORM_NAME'; export interface SharingPublicAccessFormData { enabled: boolean; permissions: PermissionLevel; + permissionUuid: string; } export interface SharingManagementFormData { permissions: SharingManagementFormDataRow[]; + initialPermissions: SharingManagementFormDataRow[]; } export interface SharingManagementFormDataRow { email: string; permissions: PermissionLevel; + permissionUuid: string; } export interface SharingInvitationFormData { -- 2.30.2