From 64961199bad8cd184befafdccb4a1d3023c4b33b Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Fri, 2 Jul 2021 17:58:18 -0400 Subject: [PATCH] :zap: Add file/folder share operation (#1951) * :zap: Add file/folder share operation * :zap: Improvements * :zap: Minor fixes Co-authored-by: Jan Oberhauser --- packages/nodes-base/nodes/Box/Box.node.ts | 123 +++++ .../nodes-base/nodes/Box/FileDescription.ts | 241 ++++++++++ .../nodes-base/nodes/Box/FolderDescription.ts | 424 ++++++++++++++++++ 3 files changed, 788 insertions(+) diff --git a/packages/nodes-base/nodes/Box/Box.node.ts b/packages/nodes-base/nodes/Box/Box.node.ts index 75006e46c..9d462b60b 100644 --- a/packages/nodes-base/nodes/Box/Box.node.ts +++ b/packages/nodes-base/nodes/Box/Box.node.ts @@ -29,6 +29,10 @@ import { import * as moment from 'moment-timezone'; +import { + noCase, +} from 'change-case'; + export class Box implements INodeType { description: INodeTypeDescription = { displayName: 'Box', @@ -81,6 +85,7 @@ export class Box implements INodeType { const length = items.length as unknown as number; const qs: IDataObject = {}; let responseData; + const timezone = this.getTimezone(); const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; for (let i = 0; i < length; i++) { @@ -199,6 +204,51 @@ export class Box implements INodeType { } returnData.push.apply(returnData, responseData as IDataObject[]); } + // https://developer.box.com/reference/post-collaborations/ + if (operation === 'share') { + const fileId = this.getNodeParameter('fileId', i) as string; + const role = this.getNodeParameter('role', i) as string; + const accessibleBy = this.getNodeParameter('accessibleBy', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + // tslint:disable-next-line: no-any + const body: { accessible_by: IDataObject, [key: string]: any } = { + accessible_by: {}, + item: { + id: fileId, + type: 'file', + }, + role: (role === 'coOwner') ? 'co-owner' : noCase(role), + ...options, + }; + + if (body.fields) { + qs.fields = body.fields; + delete body.fields; + } + + if (body.expires_at) { + body.expires_at = moment.tz(body.expires_at, timezone).format(); + } + + if (body.notify) { + qs.notify = body.notify; + delete body.notify; + } + + if (accessibleBy === 'user') { + const useEmail = this.getNodeParameter('useEmail', i) as boolean; + if (useEmail) { + body.accessible_by['login'] = this.getNodeParameter('email', i) as string; + } else { + body.accessible_by['id'] = this.getNodeParameter('userId', i) as string; + } + } else { + body.accessible_by['id'] = this.getNodeParameter('groupId', i) as string; + } + + responseData = await boxApiRequest.call(this, 'POST', `/collaborations`, body, qs); + returnData.push(responseData as IDataObject); + } // https://developer.box.com/reference/post-files-content if (operation === 'upload') { const parentId = this.getNodeParameter('parentId', i) as string; @@ -356,6 +406,79 @@ export class Box implements INodeType { } returnData.push.apply(returnData, responseData as IDataObject[]); } + // https://developer.box.com/reference/post-collaborations/ + if (operation === 'share') { + const folderId = this.getNodeParameter('folderId', i) as string; + const role = this.getNodeParameter('role', i) as string; + const accessibleBy = this.getNodeParameter('accessibleBy', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + // tslint:disable-next-line: no-any + const body: { accessible_by: IDataObject, [key: string]: any } = { + accessible_by: {}, + item: { + id: folderId, + type: 'folder', + }, + role: (role === 'coOwner') ? 'co-owner' : noCase(role), + ...options, + }; + + if (body.fields) { + qs.fields = body.fields; + delete body.fields; + } + + if (body.expires_at) { + body.expires_at = moment.tz(body.expires_at, timezone).format(); + } + + if (body.notify) { + qs.notify = body.notify; + delete body.notify; + } + + if (accessibleBy === 'user') { + const useEmail = this.getNodeParameter('useEmail', i) as boolean; + if (useEmail) { + body.accessible_by['login'] = this.getNodeParameter('email', i) as string; + } else { + body.accessible_by['id'] = this.getNodeParameter('userId', i) as string; + } + } else { + body.accessible_by['id'] = this.getNodeParameter('groupId', i) as string; + } + + responseData = await boxApiRequest.call(this, 'POST', `/collaborations`, body, qs); + returnData.push(responseData as IDataObject); + } + //https://developer.box.com/guides/folders/single/move/ + if (operation === 'update') { + const folderId = this.getNodeParameter('folderId', i) as string; + const updateFields = this.getNodeParameter('updateFields', i) as IDataObject; + + if (updateFields.fields) { + qs.fields = updateFields.fields; + delete updateFields.fields; + } + + const body = { + ...updateFields, + } as IDataObject; + + if (body.parentId) { + body.parent = { + id: body.parentId, + }; + delete body.parentId; + } + + if (body.tags) { + body.tags = (body.tags as string).split(','); + } + + responseData = await boxApiRequest.call(this, 'PUT', `/folders/${folderId}`, body, qs); + returnData.push(responseData as IDataObject); + } } } if (resource === 'file' && operation === 'download') { diff --git a/packages/nodes-base/nodes/Box/FileDescription.ts b/packages/nodes-base/nodes/Box/FileDescription.ts index 9a3ae0e2c..7aa5a87d7 100644 --- a/packages/nodes-base/nodes/Box/FileDescription.ts +++ b/packages/nodes-base/nodes/Box/FileDescription.ts @@ -40,6 +40,11 @@ export const fileOperations = [ value: 'search', description: 'Search files', }, + { + name: 'Share', + value: 'share', + description: 'Share a file', + }, { name: 'Upload', value: 'upload', @@ -496,6 +501,242 @@ export const fileFields = [ ], }, + /* -------------------------------------------------------------------------- */ + /* file:share */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'File ID', + name: 'fileId', + type: 'string', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'file', + ], + }, + }, + default: '', + description: 'The ID of the file to share.', + }, + { + displayName: 'Accessible By', + name: 'accessibleBy', + type: 'options', + options: [ + { + name: 'Group', + value: 'group', + }, + { + name: 'User', + value: 'user', + }, + ], + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'file', + ], + }, + }, + default: '', + description: 'The type of object the file will be shared with.', + }, + { + displayName: 'Use Email', + name: 'useEmail', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'file', + ], + accessibleBy: [ + 'user', + ], + }, + }, + default: true, + description: 'Whether identify the user by email or ID.', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'file', + ], + useEmail: [ + true, + ], + accessibleBy: [ + 'user', + ], + }, + }, + default: '', + description: `The user's email address to share the file with.`, + }, + { + displayName: 'User ID', + name: 'userId', + type: 'string', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'file', + ], + useEmail: [ + false, + ], + accessibleBy: [ + 'user', + ], + }, + }, + default: '', + description: `The user's ID to share the file with.`, + }, + { + displayName: 'Group ID', + name: 'groupId', + type: 'string', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'file', + ], + accessibleBy: [ + 'group', + ], + }, + }, + default: '', + description: `The group's ID to share the file with.`, + }, + { + displayName: 'Role', + name: 'role', + type: 'options', + options: [ + { + name: 'Co-Owner', + value: 'coOwner', + description: 'A Co-owner has all of functional read/write access that an editor does', + }, + { + name: 'Editor', + value: 'editor', + description: 'An editor has full read/write access to a folder or file', + }, + { + name: 'Previewer', + value: 'previewer', + description: 'A previewer has limited read access', + }, + { + name: 'Previewer Uploader', + value: 'previewerUploader', + description: 'This access level is a combination of Previewer and Uploader', + }, + { + name: 'Uploader', + value: 'uploader', + description: 'An uploader has limited write access', + }, + { + name: 'Viewer', + value: 'viewer', + description: 'A viewer has read access to a folder or file', + }, + { + name: 'Viewer Uploader', + value: 'viewerUploader', + description: 'This access level is a combination of Viewer and Uploader', + }, + ], + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'file', + ], + }, + }, + default: 'editor', + description: 'The level of access granted.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'file', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Can View Path', + name: 'can_view_path', + type: 'boolean', + default: false, + description: `Whether the invited users can see the entire parent path to the associated folder.
+ The user will not gain privileges in any parent folder and therefore cannot see content the user is not collaborated on.`, + }, + { + displayName: 'Expires At', + name: 'expires_at', + type: 'dateTime', + default: '', + description: 'Set the expiration date for the collaboration. At this date, the collaboration will be automatically removed from the item.', + }, + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: '', + description: 'A comma-separated list of attributes to include in the response. This can be used to request fields that are not normally returned in a standard response.', + }, + { + displayName: 'Notify', + name: 'notify', + type: 'boolean', + default: false, + description: 'Whether if users should receive email notification for the action performed.', + }, + ], + }, + /* -------------------------------------------------------------------------- */ /* file:upload */ /* -------------------------------------------------------------------------- */ diff --git a/packages/nodes-base/nodes/Box/FolderDescription.ts b/packages/nodes-base/nodes/Box/FolderDescription.ts index 5e944b25d..11ed33c2c 100644 --- a/packages/nodes-base/nodes/Box/FolderDescription.ts +++ b/packages/nodes-base/nodes/Box/FolderDescription.ts @@ -35,6 +35,16 @@ export const folderOperations = [ value: 'search', description: 'Search files', }, + { + name: 'Share', + value: 'share', + description: 'Share a folder', + }, + { + name: 'Update', + value: 'update', + description: 'Update folder', + }, ], default: 'create', description: 'The operation to perform.', @@ -147,6 +157,7 @@ export const folderFields = [ default: '', description: 'Folder ID', }, + /* -------------------------------------------------------------------------- */ /* folder:delete */ /* -------------------------------------------------------------------------- */ @@ -441,4 +452,417 @@ export const folderFields = [ }, ], }, + + /* -------------------------------------------------------------------------- */ + /* folder:share */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Folder ID', + name: 'folderId', + type: 'string', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'folder', + ], + }, + }, + default: '', + description: 'The ID of the folder to share.', + }, + { + displayName: 'Accessible By', + name: 'accessibleBy', + type: 'options', + options: [ + { + name: 'User', + value: 'user', + }, + { + name: 'Group', + value: 'group', + }, + ], + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'folder', + ], + }, + }, + default: 'user', + description: 'The type of object the file will be shared with.', + }, + { + displayName: 'Use Email', + name: 'useEmail', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'folder', + ], + accessibleBy: [ + 'user', + ], + }, + }, + default: true, + description: 'Whether identify the user by email or ID.', + }, + { + displayName: 'Email', + name: 'email', + type: 'string', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'folder', + ], + accessibleBy: [ + 'user', + ], + useEmail: [ + true, + ], + }, + }, + default: '', + description: `The user's email address to share the folder with.`, + }, + { + displayName: 'User ID', + name: 'userId', + type: 'string', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'folder', + ], + accessibleBy: [ + 'user', + ], + useEmail: [ + false, + ], + }, + }, + default: '', + description: `The user's ID to share the folder with.`, + }, + { + displayName: 'Group ID', + name: 'groupId', + type: 'string', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'folder', + ], + accessibleBy: [ + 'group', + ], + }, + }, + default: '', + description: `The group's ID to share the folder with.`, + }, + { + displayName: 'Role', + name: 'role', + type: 'options', + options: [ + { + name: 'Co-Owner', + value: 'coOwner', + description: 'A Co-owner has all of functional read/write access that an editor does', + }, + { + name: 'Editor', + value: 'editor', + description: 'An editor has full read/write access to a folder or file', + }, + { + name: 'Previewer', + value: 'previewer', + description: 'A previewer has limited read access', + }, + { + name: 'Previewer Uploader', + value: 'previewerUploader', + description: 'This access level is a combination of Previewer and Uploader', + }, + { + name: 'Uploader', + value: 'uploader', + description: 'An uploader has limited write access', + }, + { + name: 'Viewer', + value: 'viewer', + description: 'A viewer has read access to a folder or file', + }, + { + name: 'Viewer Uploader', + value: 'viewerUploader', + description: 'This access level is a combination of Viewer and Uploader', + }, + ], + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'folder', + ], + }, + }, + default: 'editor', + description: 'The level of access granted.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + displayOptions: { + show: { + operation: [ + 'share', + ], + resource: [ + 'folder', + ], + }, + }, + default: {}, + options: [ + { + displayName: 'Can View Path', + name: 'can_view_path', + type: 'boolean', + default: false, + description: `Whether the invited users can see the entire parent path to the associated folder.
+ The user will not gain privileges in any parent folder and therefore cannot see content the user is not collaborated on.`, + }, + { + displayName: 'Expires At', + name: 'expires_at', + type: 'dateTime', + default: '', + description: 'Set the expiration date for the collaboration. At this date, the collaboration will be automatically removed from the item.', + }, + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: '', + description: 'A comma-separated list of attributes to include in the response. This can be used to request fields that are not normally returned in a standard response.', + }, + { + displayName: 'Notify', + name: 'notify', + type: 'boolean', + default: false, + description: 'Whether if users should receive email notification for the action performed.', + }, + ], + }, + + /* -------------------------------------------------------------------------- */ + /* folder:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Folder ID', + name: 'folderId', + required: true, + type: 'string', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'folder', + ], + }, + }, + default: '', + description: 'Folder ID', + }, + { + displayName: 'Update Fields', + name: 'updateFields', + type: 'collection', + displayOptions: { + show: { + operation: [ + 'update', + ], + resource: [ + 'folder', + ], + }, + }, + default: {}, + placeholder: 'Add Field', + options: [ + { + displayName: 'Can Non-Owners Invite', + name: 'can_non_owners_invite', + type: 'boolean', + default: false, + description: 'Specifies if users who are not the owner of the folder can invite new collaborators to the folder.', + }, + { + displayName: 'Can Non-Owners View Colaborators', + name: 'can_non_owners_view_collaborators', + type: 'boolean', + default: false, + description: 'Restricts collaborators who are not the owner of this folder from viewing other collaborations on this folder.', + }, + { + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'The optional description of this folder.', + }, + { + displayName: 'Fields', + name: 'fields', + type: 'string', + default: '', + description: 'A comma-separated list of attributes to include in the response. This can be used to request fields that are not normally returned in a standard response.', + }, + { + displayName: 'Is Collaboration Restricted To Enterprise', + name: 'is_collaboration_restricted_to_enterprise', + type: 'boolean', + default: false, + description: 'Specifies if new invites to this folder are restricted to users within the enterprise. This does not affect existing collaborations.', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'The optional new name for this folder.', + }, + { + displayName: 'Parent ID', + name: 'parentId', + type: 'string', + default: '', + description: 'The parent folder for this folder. Use this to move the folder or to restore it out of the trash.', + }, + { + displayName: 'Shared Link', + name: 'shared_link', + type: 'collection', + typeOptions: { + multipleValues: false, + }, + description: 'Share link information.', + placeholder: 'Add Shared Link Config', + default: {}, + options: [ + { + displayName: 'Access', + name: 'access', + type: 'options', + options: [ + { + name: 'Collaborators', + value: 'collaborators', + description: 'Only those who have been invited to the folder', + }, + { + name: 'Company', + value: 'company', + description: 'only people within the company', + }, + { + name: 'Open', + value: 'open', + description: 'Anyone with the link', + }, + ], + default: 'open', + }, + { + displayName: 'Password', + name: 'password', + type: 'string', + displayOptions: { + show: { + access: [ + 'open', + ], + }, + }, + default: '', + description: 'The password required to access the shared link. Set the password to null to remove it.', + }, + { + displayName: 'Permissions', + name: 'permissions', + type: 'collection', + placeholder: 'Add Permition', + default: {}, + options: [ + { + displayName: 'Can Download', + name: 'can_download', + type: 'boolean', + default: false, + description: 'If the shared link allows for downloading of files.', + }, + { + displayName: 'Unshared At', + name: 'unshared_at', + type: 'dateTime', + default: '', + description: 'The timestamp at which this shared link will expire.', + }, + { + displayName: 'Vanity Name', + name: 'vanity_name', + type: 'string', + default: '', + description: 'Defines a custom vanity name to use in the shared link URL, for example https://app.box.com/v/my-shared-link.', + }, + ], + }, + { + displayName: 'Tags', + name: 'tags', + type: 'string', + default: '', + description: 'The tags for this item. These tags are shown in the Box web app and mobile apps next to an item.', + }, + ], + }, + ], + }, ] as INodeProperties[];