Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/common/types/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ type AccessibleByUserOrGroup = {
type CollaborationOptions = {
expires_at: string | null,
id: number | string,
invite_email?: string,
role: string,
status?: string,
};
Expand Down
9 changes: 5 additions & 4 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,14 @@ export const CLIENT_NAME_FOLDER_PICKER: 'FolderPicker' = 'FolderPicker';
export const CLIENT_VERSION = __VERSION__;

/* ---------------------- Statuses -------------------------- */
export const STATUS_PENDING: 'pending' = 'pending';
export const STATUS_IN_PROGRESS: 'inprogress' = 'inprogress';
export const STATUS_STAGED: 'staged' = 'staged';
export const STATUS_ACCEPTED: 'accepted' = 'accepted';
export const STATUS_COMPLETE: 'complete' = 'complete';
export const STATUS_ERROR: 'error' = 'error';
export const STATUS_ACCEPTED: 'accepted' = 'accepted';
export const STATUS_INACTIVE: 'inactive' = 'inactive';
export const STATUS_IN_PROGRESS: 'inprogress' = 'inprogress';
Comment thread
jpan-box marked this conversation as resolved.
export const STATUS_PENDING: 'pending' = 'pending';
export const STATUS_REJECTED: 'rejected' = 'rejected';
export const STATUS_STAGED: 'staged' = 'staged';

/* ------------------- Styles ------------------------ */
export const CLASS_MODAL_CONTENT = 'be-modal-dialog-content';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { STATUS_ACCEPTED } from '../../../../constants';
import { STATUS_ACCEPTED, STATUS_PENDING, STATUS_REJECTED } from '../../../../constants';
import { convertCollab, convertCollabsResponse, convertCollabsRequest } from '../convertCollaborators';
import {
collabUser1,
Expand Down Expand Up @@ -50,11 +50,39 @@ const mockCollaborationsFromApi: Collaborations = {
{
id: '125',
role: 'editor',
status: 'pending',
status: STATUS_PENDING,
expires_at: '2024-12-31T23:59:59Z',
accessible_by: collabUser3,
created_by: ownerFromApi,
},
{
id: '126',
role: 'editor',
status: STATUS_PENDING,
expires_at: '2024-12-31T23:59:59Z',
accessible_by: {
id: '126',
},
created_by: ownerFromApi,
},
{
id: '127',
role: 'editor',
status: STATUS_PENDING,
expires_at: '2024-12-31T23:59:59Z',
accessible_by: null,
created_by: ownerFromApi,
invite_email: 'bbear@external.example.com',
},
{
id: '128',
role: 'editor',
status: STATUS_REJECTED,
expires_at: '2024-12-31T23:59:59Z',
accessible_by: null,
created_by: ownerFromApi,
invite_email: 'rrobot@external.example.com',
},
],
};

Expand Down Expand Up @@ -86,7 +114,30 @@ describe('convertCollaborators', () => {
});
});

test('should return null for collaboration with non-accepted status', () => {
test('should convert pending collaboration with invite_email to a pending collaborator', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[5],
currentUserId: mockOwnerId,
isCurrentUserOwner: false,
ownerEmailDomain,
});

expect(result).toEqual({
avatarUrl: undefined,
email: 'bbear@external.example.com',
expiresAt: '2024-12-31T23:59:59Z',
hasCustomAvatar: false,
id: '127',
isCurrentUser: false,
isExternal: true,
isPending: true,
name: 'bbear@external.example.com',
role: 'editor',
});
});

test('should convert pending collaboration with accessible_by', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[3],
Expand All @@ -95,7 +146,19 @@ describe('convertCollaborators', () => {
ownerEmailDomain,
});

expect(result).toBeNull();
expect(result).toEqual({
avatarUrl: undefined,
email: 'dpenguin@example.com',
expiresAt: '2024-12-31T23:59:59Z',
hasCustomAvatar: false,
id: '125',
isCurrentUser: false,
isExternal: false,
isPending: true,
name: 'Dancing Penguin',
role: 'editor',
userId: '458',
});
});

test.each([undefined, null])('should return null for %s collaboration', collab => {
Expand All @@ -110,6 +173,30 @@ describe('convertCollaborators', () => {
expect(result).toBeNull();
});

test('should return null for pending collab with accessible_by without collab name', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[4],
currentUserId: mockOwnerId,
isCurrentUserOwner: false,
ownerEmailDomain,
});

expect(result).toBeNull();
});

test('should return null for rejected collab', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
collab: mockCollaborations[6],
currentUserId: mockOwnerId,
isCurrentUserOwner: false,
ownerEmailDomain,
});

expect(result).toBeNull();
});

test('should identify current user correctly', () => {
const result = convertCollab({
avatarUrlMap: mockAvatarUrlMap,
Expand Down Expand Up @@ -188,7 +275,7 @@ describe('convertCollaborators', () => {
mockAvatarUrlMap,
);

expect(result).toHaveLength(3); // Only accepted collaborations
expect(result).toHaveLength(5); // Owner + collaborators (rejected and unnamed pending omitted)
expect(result).toEqual([
{
avatarUrl: undefined,
Expand Down Expand Up @@ -229,6 +316,31 @@ describe('convertCollaborators', () => {
role: 'viewer',
userId: '457',
},
{
avatarUrl: undefined,
email: 'dpenguin@example.com',
expiresAt: '2024-12-31T23:59:59Z',
hasCustomAvatar: false,
id: '125',
isCurrentUser: false,
isExternal: false,
isPending: true,
name: 'Dancing Penguin',
role: 'editor',
userId: '458',
},
{
avatarUrl: undefined,
email: 'bbear@external.example.com',
expiresAt: '2024-12-31T23:59:59Z',
hasCustomAvatar: false,
id: '127',
isCurrentUser: false,
isExternal: false,
isPending: true,
name: 'bbear@external.example.com',
role: 'editor',
},
]);
});

Expand Down
40 changes: 26 additions & 14 deletions src/elements/content-sharing/utils/convertCollaborators.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Collaborator } from '@box/unified-share-modal';

import { INVITEE_ROLE_OWNER, STATUS_ACCEPTED } from '../../../constants';
import { INVITEE_ROLE_OWNER, STATUS_ACCEPTED, STATUS_PENDING, STATUS_REJECTED } from '../../../constants';
import {
API_TO_USM_COLLAB_ROLE_MAP,
COLLAB_USER_TYPE,
Expand All @@ -26,32 +26,44 @@ export const convertCollab = ({
isCurrentUserOwner,
ownerEmailDomain,
}: ConvertCollabProps): Collaborator | null => {
if (!collab || collab.status !== STATUS_ACCEPTED) return null;
if (!collab || collab.status === STATUS_REJECTED) {
return null;
}

const { accessible_by: accessibleBy, id, invite_email: inviteEmail, expires_at: expiresAt, role, status } = collab;

let collabId;
let collabEmail = inviteEmail;
let collabName = inviteEmail;

if (accessibleBy?.name) {
const { id: userId, login, name } = accessibleBy;

const {
accessible_by: { id: collabId, login: collabEmail, name: collabName },
id,
expires_at: executeAt,
role,
} = collab;
collabId = userId;
collabEmail = login;
collabName = name;
}

if (!collabName) {
return null;
}
Comment thread
reneshen0328 marked this conversation as resolved.

const isCurrentUser = collabId === currentUserId;
const isExternal =
!isCurrentUserOwner && collabEmail && ownerEmailDomain && collabEmail.split('@')[1] !== ownerEmailDomain;
!isCurrentUserOwner && !!collabEmail && !!ownerEmailDomain && collabEmail.split('@')[1] !== ownerEmailDomain;
const avatarUrl = avatarUrlMap ? avatarUrlMap[collabId] : undefined;

return {
avatarUrl,
email: collabEmail,
expiresAt: executeAt,
expiresAt,
hasCustomAvatar: !!avatarUrl,
id: `${id}`,
isCurrentUser,
isCurrentUser: collabId != null && collabId === currentUserId,
Comment thread
reneshen0328 marked this conversation as resolved.
isExternal,
isPending: false,
isPending: status === STATUS_PENDING,
name: collabName,
role: API_TO_USM_COLLAB_ROLE_MAP[role],
userId: `${collabId}`,
...(collabId != null ? { userId: `${collabId}` } : {}),
Comment thread
reneshen0328 marked this conversation as resolved.
};
};

Expand Down
Loading