X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/54970f239795f4b957febefbc514572e0d19633c..282606d6586ed26675ef27f9f53cfeb0375dafc8:/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 bbd50dbb..985b345e 100644 --- a/src/store/sharing-dialog/sharing-dialog-actions.ts +++ b/src/store/sharing-dialog/sharing-dialog-actions.ts @@ -2,29 +2,39 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { dialogActions } from "~/store/dialog/dialog-actions"; -import { withDialog } from "~/store/dialog/with-dialog"; -import { SHARING_DIALOG_NAME, SharingPublicAccessFormData, SHARING_PUBLIC_ACCESS_FORM_NAME, SHARING_INVITATION_FORM_NAME, SharingManagementFormData, SharingInvitationFormData, VisibilityLevel, getSharingMangementFormData, getSharingPublicAccessFormData } from './sharing-dialog-types'; +import { dialogActions } from "store/dialog/dialog-actions"; +import { withDialog } from "store/dialog/with-dialog"; +import { + SHARING_DIALOG_NAME, + SHARING_INVITATION_FORM_NAME, + SharingManagementFormData, + SharingInvitationFormData, + getSharingMangementFormData, +} from './sharing-dialog-types'; import { Dispatch } from 'redux'; -import { ServiceRepository } from "~/services/services"; -import { FilterBuilder } from '~/services/api/filter-builder'; +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 { 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 { 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 { withProgress } from "store/progress-indicator/with-progress"; +import { progressIndicatorActions } from 'store/progress-indicator/progress-indicator-actions'; import { snackbarActions, SnackbarKind } from "../snackbar/snackbar-actions"; -import { extractUuidKind, ResourceKind } from "~/models/resource"; -import { LinkClass } from "~/models/link"; - -export const openSharingDialog = (resourceUuid: string) => +import { + extractUuidKind, + extractUuidObjectType, + ResourceKind, + ResourceObjectType +} from "models/resource"; +import { resourcesActions } from "store/resources/resources-actions"; + +export const openSharingDialog = (resourceUuid: string, refresh?: () => void) => (dispatch: Dispatch) => { - dispatch(dialogActions.OPEN_DIALOG({ id: SHARING_DIALOG_NAME, data: resourceUuid })); + dispatch(dialogActions.OPEN_DIALOG({ id: SHARING_DIALOG_NAME, data: {resourceUuid, refresh} })); dispatch(loadSharingDialog); }; @@ -35,16 +45,21 @@ export const connectSharingDialog = withDialog(SHARING_DIALOG_NAME); export const connectSharingDialogProgress = withProgress(SHARING_DIALOG_NAME); -export const saveSharingDialogChanges = async (dispatch: Dispatch) => { +export const saveSharingDialogChanges = async (dispatch: Dispatch, getState: () => RootState) => { 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); + dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); + + const dialog = getDialog(getState().dialog, SHARING_DIALOG_NAME); + if (dialog && dialog.data.refresh) { + dialog.data.refresh(); + } }; -export const sendSharingInvitations = async (dispatch: Dispatch) => { +export const sendSharingInvitations = async (dispatch: Dispatch, getState: () => RootState) => { dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME)); await dispatch(sendInvitations); dispatch(closeSharingDialog()); @@ -53,21 +68,87 @@ export const sendSharingInvitations = async (dispatch: Dispatch) => { kind: SnackbarKind.SUCCESS, })); dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); + + const dialog = getDialog(getState().dialog, SHARING_DIALOG_NAME); + if (dialog && dialog.data.refresh) { + dialog.data.refresh(); + } }; -const loadSharingDialog = async (dispatch: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { +export interface SharingDialogData { + resourceUuid: string; + refresh: () => void; +} - const dialog = getDialog(getState().dialog, SHARING_DIALOG_NAME); +export const createSharingToken = async (dispatch: Dispatch, getState: () => RootState, { apiClientAuthorizationService }: ServiceRepository) => { + const dialog = getDialog(getState().dialog, SHARING_DIALOG_NAME); + if (dialog) { + const resourceUuid = dialog.data.resourceUuid; + if (extractUuidObjectType(resourceUuid) === ResourceObjectType.COLLECTION) { + dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME)); + try { + const sharingToken = await apiClientAuthorizationService.createCollectionSharingToken(resourceUuid); + dispatch(resourcesActions.SET_RESOURCES([sharingToken])); + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Sharing URL created', + hideDuration: 2000, + kind: SnackbarKind.SUCCESS, + })); + } catch (e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Failed to create sharing URL', + hideDuration: 2000, + kind: SnackbarKind.ERROR, + })); + } finally { + dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); + } + } + } +}; + +export const deleteSharingToken = (uuid: string) => async (dispatch: Dispatch, getState: () => RootState, { apiClientAuthorizationService }: ServiceRepository) => { + dispatch(progressIndicatorActions.START_WORKING(SHARING_DIALOG_NAME)); + try { + await apiClientAuthorizationService.delete(uuid); + dispatch(resourcesActions.DELETE_RESOURCES([uuid])); + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Sharing URL removed', + hideDuration: 2000, + kind: SnackbarKind.SUCCESS, + })); + } catch (e) { + dispatch(snackbarActions.OPEN_SNACKBAR({ + message: 'Failed to remove sharing URL', + hideDuration: 2000, + kind: SnackbarKind.ERROR, + })); + } finally { + dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); + } +}; + +const loadSharingDialog = async (dispatch: Dispatch, getState: () => RootState, { permissionService, apiClientAuthorizationService }: 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)); + const resourceUuid = dialog.data.resourceUuid; + const { items } = await permissionService.listResourcePermissions(resourceUuid); await dispatch(initializeManagementForm(items)); - dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); + // For collections, we need to load the public sharing tokens + if (extractUuidObjectType(resourceUuid) === ResourceObjectType.COLLECTION) { + const sharingTokens = await apiClientAuthorizationService.listCollectionSharingTokens(resourceUuid); + dispatch(resourcesActions.SET_RESOURCES([...sharingTokens.items])); + } } catch (e) { - dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'You do not have access to share this item', hideDuration: 2000, kind: SnackbarKind.ERROR })); + 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 })); + } finally { dispatch(progressIndicatorActions.STOP_WORKING(SHARING_DIALOG_NAME)); } } @@ -80,8 +161,8 @@ const initializeManagementForm = (permissionLinks: PermissionResource[]) => .addIn('uuid', permissionLinks.map(({ tailUuid }) => tailUuid)) .getFilters(); - const { items: users } = await userService.list({ filters }); - const { items: groups} = await groupsService.list({ filters }); + const { items: users } = await userService.list({ filters, count: "none" }); + const { items: groups } = await groupsService.list({ filters, count: "none" }); const getEmail = (tailUuid: string) => { const user = users.find(({ uuid }) => uuid === tailUuid); @@ -94,8 +175,6 @@ const initializeManagementForm = (permissionLinks: PermissionResource[]) => }; const managementPermissions = permissionLinks - .filter(item => - item.tailUuid !== getPublicGroupUuid(getState())) .map(({ tailUuid, name, uuid }) => ({ email: getEmail(tailUuid), permissions: name as PermissionLevel, @@ -110,129 +189,51 @@ const initializeManagementForm = (permissionLinks: PermissionResource[]) => 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); - } + const cancelledPermissions = differenceWith( + initialPermissions, + permissions, + (a, b) => a.permissionUuid === b.permissionUuid + ); - for (const permission of permissions) { - await permissionService.update(permission.permissionUuid, { name: permission.permissions }); - } + 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, userService }: ServiceRepository) => { +const sendInvitations = async (_: Dispatch, getState: () => RootState, { permissionService }: ServiceRepository) => { const state = getState(); const { user } = state.auth; - const dialog = getDialog(state.dialog, SHARING_DIALOG_NAME); + const dialog = getDialog(state.dialog, SHARING_DIALOG_NAME); if (dialog && user) { const invitations = getFormValues(SHARING_INVITATION_FORM_NAME)(state) as SharingInvitationFormData; const getGroupsFromForm = invitations.invitedPeople.filter((invitation) => extractUuidKind(invitation.uuid) === ResourceKind.GROUP); const getUsersFromForm = invitations.invitedPeople.filter((invitation) => extractUuidKind(invitation.uuid) === ResourceKind.USER); - const uuids = getGroupsFromForm.map(group => group.uuid); - - const permissions = await permissionService.list({ - filters: new FilterBuilder() - .addIn('tailUuid', uuids) - .addEqual('linkClass', LinkClass.PERMISSION) - .getFilters() - }); - - const usersFromGroups = await userService.list({ - filters: new FilterBuilder() - .addIn('uuid', permissions.items.map(item => item.headUuid)) - .getFilters() - - }); const invitationDataUsers = getUsersFromForm .map(person => ({ ownerUuid: user.uuid, - headUuid: dialog.data, + headUuid: dialog.data.resourceUuid, tailUuid: person.uuid, name: invitations.permissions })); - const invitationsDataGroups = usersFromGroups.items.map( - person => ({ + const invitationsDataGroups = getGroupsFromForm.map( + group => ({ ownerUuid: user.uuid, - headUuid: dialog.data, - tailUuid: person.uuid, + headUuid: dialog.data.resourceUuid, + tailUuid: group.uuid, name: invitations.permissions }) );