import { request } from "@esri/arcgis-rest-request" ; import { IUser } from "@esri/arcgis-rest-types" ; import { getPortalUrl } from "../util/get-portal-url" ; import { IGroupSharingOptions , ISharingResponse , getUserMembership , } from "./helpers" ; import { getUser } from "../users/get-user" ; import { addGroupUsers , IAddGroupUsersResult } from "../groups/add-users" ; import { removeGroupUsers } from "../groups/remove-users" ; import { updateUserMemberships , IUpdateGroupUsersResult , } from "../groups/update-user-membership" ; import { isItemSharedWithGroup } from "../sharing/is-item-shared-with-group" ; interface IEnsureMembershipResult { promise : Promise < IAddGroupUsersResult > ; revert : ( sharingResults : ISharingResponse ) => Promise < ISharingResponse > ; } param requestOptions returns export function shareItemWithGroup ( requestOptions ) { return isItemSharedWithGroup ( requestOptions ) . then ( ( isShared ) => { if ( isShared ) { return { itemId : requestOptions . id , shortcut : true , notSharedWith : [ ] , } as ISharingResponse ; } const { authentication : { username } , owner , confirmItemControl , } = requestOptions ; const itemOwner = owner || username ; if ( itemOwner !== username ) { let isAdmin = false ; let isCrossOrgSharing = false ; return Promise . all ( [ getUser ( { username , authentication : requestOptions . authentication , } ) , getUser ( { username : itemOwner , authentication : requestOptions . authentication , } ) , getUserMembership ( requestOptions ) , ] ) . then ( ( [ currentUser , ownerUser , membership ] ) => { const isSharedEditingGroup = ! ! confirmItemControl ; isAdmin = currentUser . role === "org_admin" && ! currentUser . roleId ; isCrossOrgSharing = currentUser . orgId !== ownerUser . orgId ; return getMembershipAdjustments ( currentUser , isSharedEditingGroup , membership , isAdmin , ownerUser , requestOptions ) ; } ) . then ( ( membershipAdjustments ) => { const [ { revert } = { promise : Promise . resolve ( { notAdded : [ ] } ) , revert : ( sharingResults ) => { return Promise . resolve ( sharingResults ) ; } , } as IEnsureMembershipResult , ] = membershipAdjustments ; return Promise . all ( membershipAdjustments . map ( ( { promise } ) => promise ) ) . then ( ( ) => { return shareToGroup ( requestOptions , isAdmin , isCrossOrgSharing ) ; } ) . then ( ( sharingResults ) => { return revert ( sharingResults ) ; } ) ; } ) ; } return shareToGroup ( requestOptions ) ; } ) . then ( ( sharingResponse ) => { if ( sharingResponse . notSharedWith . length ) { throw Error ( `Item ${ requestOptions . id } could not be shared to group ${ requestOptions . groupId }.` ) ; } else { return sharingResponse ; } } ) ; } function getMembershipAdjustments ( currentUser , isSharedEditingGroup , membership , isAdmin , ownerUser , requestOptions ) { const membershipGuarantees = [ ] ; if ( requestOptions . groupId !== currentUser . favGroupId ) { if ( isSharedEditingGroup ) { if ( ! isAdmin ) { throw Error ( `This item can not be shared to shared editing group ${ requestOptions . groupId } by ${ currentUser . username } as they not the item owner or org admin.` ) ; } membershipGuarantees . push ( ensureMembership ( currentUser , currentUser , false , `Error adding ${ currentUser . username } as member to edit group ${ requestOptions . groupId }. Consequently item ${ requestOptions . id } was not shared to the group.` , requestOptions ) , ensureMembership ( currentUser , ownerUser , true , membership === "none" ? `Error adding user ${ ownerUser . username } to edit group ${ requestOptions . groupId }. Consequently item ${ requestOptions . id } was not shared to the group.` : `Error promoting user ${ ownerUser . username } to admin in edit group ${ requestOptions . groupId }. Consequently item ${ requestOptions . id } was not shared to the group.` , requestOptions ) ) ; } else if ( isAdmin ) { membershipGuarantees . push ( ensureMembership ( currentUser , currentUser , false , `Error adding ${ currentUser . username } as member to view group ${ requestOptions . groupId }. Consequently item ${ requestOptions . id } was not shared to the group.` , requestOptions ) ) ; } else if ( membership === "none" ) { throw new Error ( `This item can not be shared by ${ currentUser . username } as they are not a member of the specified group ${ requestOptions . groupId }.` ) ; } } return membershipGuarantees ; } function shareToGroup ( requestOptions , isAdmin = false , isCrossOrgSharing = false ) { const username = requestOptions . authentication . username ; const itemOwner = requestOptions . owner || username ; let url = `${ getPortalUrl ( requestOptions ) }/content/items/${ requestOptions . id }/share` ; if ( itemOwner === username || ( isAdmin && ! isCrossOrgSharing ) ) { url = `${ getPortalUrl ( requestOptions ) }/content/users/${ itemOwner }/items/${ requestOptions . id }/share` ; } requestOptions . params = { groups : requestOptions . groupId , confirmItemControl : requestOptions . confirmItemControl , } ; return request ( url , requestOptions ) ; } export function ensureMembership ( currentUser , ownerUser , shouldPromote , errorMessage , requestOptions ) { const ownerGroups = ownerUser . groups || [ ] ; const group = ownerGroups . find ( ( g ) => { return g . id === requestOptions . groupId ; } ) ; if ( currentUser . orgId !== ownerUser . orgId ) { throw Error ( `User ${ ownerUser . username } is not a member of the same org as ${ currentUser . username }. Consequently they can not be added added to group ${ requestOptions . groupId } nor can item ${ requestOptions . id } be shared to the group.` ) ; } if ( ! group && ownerGroups . length > 511 ) { throw Error ( `User ${ ownerUser . username } already has 512 groups, and can not be added to group ${ requestOptions . groupId }. Consequently item ${ requestOptions . id } can not be shared to the group.` ) ; } let promise ; let revert ; if ( group ) { if ( shouldPromote && group . userMembership . memberType === "member" ) { promise = updateUserMemberships ( { id : requestOptions . groupId , users : [ ownerUser . username ] , newMemberType : "admin" , authentication : requestOptions . authentication , } ) . then ( ( results ) => { const notAdded = results . results . reduce ( ( acc , entry ) => { if ( ! entry . success ) { acc . push ( entry . username ) ; } return acc ; } , [ ] ) ; return Promise . resolve ( { notAdded } ) ; } ) . catch ( ( ) => ( { notAdded : [ ownerUser . username ] } ) ) ; revert = ( sharingResults ) => updateUserMemberships ( { id : requestOptions . groupId , users : [ ownerUser . username ] , newMemberType : "member" , authentication : requestOptions . authentication , } ) . then ( ( ) => sharingResults ) . catch ( ( ) => sharingResults ) ; } else { promise = Promise . resolve ( { notAdded : [ ] } ) ; revert = ( sharingResults ) => Promise . resolve ( sharingResults ) ; } } else { const userType = shouldPromote ? "admins" : "users" ; promise = addGroupUsers ( { id : requestOptions . groupId , [ userType ] : [ ownerUser . username ] , authentication : requestOptions . authentication , } ) . then ( ( results ) => { if ( results . errors && results . errors . length ) { throw results . errors [ 0 ] ; } return results ; } ) . catch ( ( ) => ( { notAdded : [ ownerUser . username ] } ) ) ; revert = ( sharingResults ) => { return removeGroupUsers ( { id : requestOptions . groupId , users : [ ownerUser . username ] , authentication : requestOptions . authentication , } ) . then ( ( ) => { return sharingResults ; } ) ; } ; } return { promise : promise . then ( ( membershipResponse ) => { if ( membershipResponse . notAdded . length ) { throw new Error ( errorMessage ) ; } return membershipResponse ; } ) , revert , } ; }