first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.box",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Data & Storage"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/box/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.box/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
import { noCase } from 'change-case';
|
||||
import moment from 'moment-timezone';
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeConnectionTypes, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { fileFields, fileOperations } from './FileDescription';
|
||||
import { folderFields, folderOperations } from './FolderDescription';
|
||||
import { boxApiRequest, boxApiRequestAllItems } from './GenericFunctions';
|
||||
|
||||
export class Box implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Box',
|
||||
name: 'box',
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
|
||||
icon: 'file:box.png',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Box API',
|
||||
defaults: {
|
||||
name: 'Box',
|
||||
},
|
||||
inputs: [NodeConnectionTypes.Main],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'boxOAuth2Api',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'File',
|
||||
value: 'file',
|
||||
},
|
||||
{
|
||||
name: 'Folder',
|
||||
value: 'folder',
|
||||
},
|
||||
],
|
||||
default: 'file',
|
||||
},
|
||||
...fileOperations,
|
||||
...fileFields,
|
||||
...folderOperations,
|
||||
...folderFields,
|
||||
],
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
const length = items.length;
|
||||
const qs: IDataObject = {};
|
||||
let responseData;
|
||||
const timezone = this.getTimezone();
|
||||
const resource = this.getNodeParameter('resource', 0);
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
for (let i = 0; i < length; i++) {
|
||||
try {
|
||||
if (resource === 'file') {
|
||||
// https://developer.box.com/reference/post-files-id-copy
|
||||
if (operation === 'copy') {
|
||||
const fileId = this.getNodeParameter('fileId', i) as string;
|
||||
const parentId = this.getNodeParameter('parentId', i) as string;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
const body: IDataObject = {};
|
||||
if (additionalFields.name) {
|
||||
body.name = additionalFields.name as string;
|
||||
}
|
||||
if (parentId) {
|
||||
body.parent = { id: parentId };
|
||||
} else {
|
||||
body.parent = { id: 0 };
|
||||
}
|
||||
if (additionalFields.fields) {
|
||||
qs.fields = additionalFields.fields as string;
|
||||
}
|
||||
if (additionalFields.version) {
|
||||
body.version = additionalFields.version as string;
|
||||
}
|
||||
responseData = await boxApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
`/files/${fileId}/copy`,
|
||||
body,
|
||||
qs,
|
||||
);
|
||||
}
|
||||
// https://developer.box.com/reference/delete-files-id
|
||||
if (operation === 'delete') {
|
||||
const fileId = this.getNodeParameter('fileId', i) as string;
|
||||
responseData = await boxApiRequest.call(this, 'DELETE', `/files/${fileId}`);
|
||||
responseData = { success: true };
|
||||
}
|
||||
// https://developer.box.com/reference/get-files-id-content
|
||||
if (operation === 'download') {
|
||||
const fileId = this.getNodeParameter('fileId', i) as string;
|
||||
const dataPropertyNameDownload = this.getNodeParameter('binaryPropertyName', i);
|
||||
responseData = await boxApiRequest.call(this, 'GET', `/files/${fileId}`);
|
||||
|
||||
const fileName = responseData.name;
|
||||
|
||||
let mimeType: string | undefined;
|
||||
|
||||
responseData = await boxApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/files/${fileId}/content`,
|
||||
{},
|
||||
{},
|
||||
undefined,
|
||||
{ encoding: null, resolveWithFullResponse: true },
|
||||
);
|
||||
|
||||
const newItem: INodeExecutionData = {
|
||||
json: items[i].json,
|
||||
binary: {},
|
||||
};
|
||||
|
||||
if (mimeType === undefined && responseData.headers['content-type']) {
|
||||
mimeType = responseData.headers['content-type'];
|
||||
}
|
||||
|
||||
if (items[i].binary !== undefined) {
|
||||
// Create a shallow copy of the binary data so that the old
|
||||
// data references which do not get changed still stay behind
|
||||
// but the incoming data does not get changed.
|
||||
Object.assign(newItem.binary!, items[i].binary);
|
||||
}
|
||||
|
||||
items[i] = newItem;
|
||||
|
||||
const data = Buffer.from(responseData.body as string);
|
||||
|
||||
items[i].binary![dataPropertyNameDownload] = await this.helpers.prepareBinaryData(
|
||||
data as unknown as Buffer,
|
||||
fileName as string,
|
||||
mimeType,
|
||||
);
|
||||
}
|
||||
// https://developer.box.com/reference/get-files-id
|
||||
if (operation === 'get') {
|
||||
const fileId = this.getNodeParameter('fileId', i) as string;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
if (additionalFields.fields) {
|
||||
qs.fields = additionalFields.fields as string;
|
||||
}
|
||||
responseData = await boxApiRequest.call(this, 'GET', `/files/${fileId}`, {}, qs);
|
||||
}
|
||||
// https://developer.box.com/reference/get-search/
|
||||
if (operation === 'search') {
|
||||
const query = this.getNodeParameter('query', i) as string;
|
||||
const returnAll = this.getNodeParameter('returnAll', i);
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
const tz = this.getTimezone();
|
||||
qs.type = 'file';
|
||||
qs.query = query;
|
||||
Object.assign(qs, additionalFields);
|
||||
|
||||
if (qs.content_types) {
|
||||
qs.content_types = (qs.content_types as string).split(',');
|
||||
}
|
||||
|
||||
if (additionalFields.createdRangeUi) {
|
||||
const createdRangeValues = (additionalFields.createdRangeUi as IDataObject)
|
||||
.createdRangeValuesUi as IDataObject;
|
||||
if (createdRangeValues) {
|
||||
const from = moment.tz(createdRangeValues.from, tz).format();
|
||||
const to = moment.tz(createdRangeValues.to, tz).format();
|
||||
qs.created_at_range = `${from},${to}`;
|
||||
}
|
||||
delete qs.createdRangeUi;
|
||||
}
|
||||
|
||||
if (additionalFields.updatedRangeUi) {
|
||||
const updateRangeValues = (additionalFields.updatedRangeUi as IDataObject)
|
||||
.updatedRangeValuesUi as IDataObject;
|
||||
if (updateRangeValues) {
|
||||
qs.updated_at_range = `${moment.tz(updateRangeValues.from, tz).format()},${moment
|
||||
.tz(updateRangeValues.to, tz)
|
||||
.format()}`;
|
||||
}
|
||||
delete qs.updatedRangeUi;
|
||||
}
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await boxApiRequestAllItems.call(
|
||||
this,
|
||||
'entries',
|
||||
'GET',
|
||||
'/search',
|
||||
{},
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.limit = this.getNodeParameter('limit', i);
|
||||
responseData = await boxApiRequest.call(this, 'GET', '/search', {}, qs);
|
||||
responseData = responseData.entries;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
|
||||
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);
|
||||
}
|
||||
// https://developer.box.com/reference/post-files-content
|
||||
if (operation === 'upload') {
|
||||
const parentId = this.getNodeParameter('parentId', i) as string;
|
||||
const isBinaryData = this.getNodeParameter('binaryData', i);
|
||||
const fileName = this.getNodeParameter('fileName', i) as string;
|
||||
|
||||
const attributes: IDataObject = {};
|
||||
|
||||
if (parentId !== '') {
|
||||
attributes.parent = { id: parentId };
|
||||
} else {
|
||||
// if not parent defined save it on the root directory
|
||||
attributes.parent = { id: 0 };
|
||||
}
|
||||
|
||||
if (isBinaryData) {
|
||||
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i);
|
||||
const binaryData = this.helpers.assertBinaryData(i, binaryPropertyName);
|
||||
const binaryDataBuffer = await this.helpers.getBinaryDataBuffer(
|
||||
i,
|
||||
binaryPropertyName,
|
||||
);
|
||||
|
||||
const body: IDataObject = {};
|
||||
|
||||
attributes.name = fileName || binaryData.fileName;
|
||||
|
||||
body.attributes = JSON.stringify(attributes);
|
||||
|
||||
body.file = {
|
||||
value: binaryDataBuffer,
|
||||
options: {
|
||||
filename: binaryData.fileName,
|
||||
contentType: binaryData.mimeType,
|
||||
},
|
||||
};
|
||||
|
||||
responseData = await boxApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'',
|
||||
{},
|
||||
{},
|
||||
'https://upload.box.com/api/2.0/files/content',
|
||||
{ formData: body },
|
||||
);
|
||||
responseData = responseData.entries;
|
||||
} else {
|
||||
const content = this.getNodeParameter('fileContent', i) as string;
|
||||
|
||||
if (fileName === '') {
|
||||
throw new NodeOperationError(this.getNode(), 'File name must be set!', {
|
||||
itemIndex: i,
|
||||
});
|
||||
}
|
||||
|
||||
attributes.name = fileName;
|
||||
|
||||
const body: IDataObject = {};
|
||||
|
||||
body.attributes = JSON.stringify(attributes);
|
||||
|
||||
body.file = {
|
||||
value: Buffer.from(content),
|
||||
options: {
|
||||
filename: fileName,
|
||||
contentType: 'text/plain',
|
||||
},
|
||||
};
|
||||
responseData = await boxApiRequest.call(
|
||||
this,
|
||||
'POST',
|
||||
'',
|
||||
{},
|
||||
{},
|
||||
'https://upload.box.com/api/2.0/files/content',
|
||||
{ formData: body },
|
||||
);
|
||||
responseData = responseData.entries;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (resource === 'folder') {
|
||||
// https://developer.box.com/reference/post-folders
|
||||
if (operation === 'create') {
|
||||
const name = this.getNodeParameter('name', i) as string;
|
||||
const parentId = this.getNodeParameter('parentId', i) as string;
|
||||
const options = this.getNodeParameter('options', i);
|
||||
const body: IDataObject = {
|
||||
name,
|
||||
};
|
||||
|
||||
if (parentId) {
|
||||
body.parent = { id: parentId };
|
||||
} else {
|
||||
body.parent = { id: 0 };
|
||||
}
|
||||
|
||||
if (options.access) {
|
||||
body.folder_upload_email = {
|
||||
access: options.access as string,
|
||||
};
|
||||
}
|
||||
|
||||
if (options.fields) {
|
||||
qs.fields = options.fields as string;
|
||||
}
|
||||
|
||||
responseData = await boxApiRequest.call(this, 'POST', '/folders', body, qs);
|
||||
}
|
||||
// https://developer.box.com/reference/delete-folders-id
|
||||
if (operation === 'delete') {
|
||||
const folderId = this.getNodeParameter('folderId', i) as string;
|
||||
const recursive = this.getNodeParameter('recursive', i) as boolean;
|
||||
qs.recursive = recursive;
|
||||
|
||||
responseData = await boxApiRequest.call(this, 'DELETE', `/folders/${folderId}`, qs);
|
||||
responseData = { success: true };
|
||||
}
|
||||
// https://developer.box.com/reference/get-folders-id/
|
||||
if (operation === 'get') {
|
||||
const folderId = this.getNodeParameter('folderId', i) as string;
|
||||
responseData = await boxApiRequest.call(this, 'GET', `/folders/${folderId}`, qs);
|
||||
}
|
||||
// https://developer.box.com/reference/get-search/
|
||||
if (operation === 'search') {
|
||||
const query = this.getNodeParameter('query', i) as string;
|
||||
const returnAll = this.getNodeParameter('returnAll', i);
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i);
|
||||
const tz = this.getTimezone();
|
||||
qs.type = 'folder';
|
||||
qs.query = query;
|
||||
Object.assign(qs, additionalFields);
|
||||
|
||||
if (qs.content_types) {
|
||||
qs.content_types = (qs.content_types as string).split(',');
|
||||
}
|
||||
|
||||
if (additionalFields.createdRangeUi) {
|
||||
const createdRangeValues = (additionalFields.createdRangeUi as IDataObject)
|
||||
.createdRangeValuesUi as IDataObject;
|
||||
if (createdRangeValues) {
|
||||
qs.created_at_range = `${moment.tz(createdRangeValues.from, tz).format()},${moment
|
||||
.tz(createdRangeValues.to, tz)
|
||||
.format()}`;
|
||||
}
|
||||
delete qs.createdRangeUi;
|
||||
}
|
||||
|
||||
if (additionalFields.updatedRangeUi) {
|
||||
const updateRangeValues = (additionalFields.updatedRangeUi as IDataObject)
|
||||
.updatedRangeValuesUi as IDataObject;
|
||||
if (updateRangeValues) {
|
||||
qs.updated_at_range = `${moment.tz(updateRangeValues.from, tz).format()},${moment
|
||||
.tz(updateRangeValues.to, tz)
|
||||
.format()}`;
|
||||
}
|
||||
delete qs.updatedRangeUi;
|
||||
}
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await boxApiRequestAllItems.call(
|
||||
this,
|
||||
'entries',
|
||||
'GET',
|
||||
'/search',
|
||||
{},
|
||||
qs,
|
||||
);
|
||||
} else {
|
||||
qs.limit = this.getNodeParameter('limit', i);
|
||||
responseData = await boxApiRequest.call(this, 'GET', '/search', {}, qs);
|
||||
responseData = responseData.entries;
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
|
||||
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);
|
||||
}
|
||||
//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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
const executionData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray(responseData as IDataObject[]),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
returnData.push(...executionData);
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
const executionErrorData = this.helpers.constructExecutionMetaData(
|
||||
this.helpers.returnJsonArray({ error: error.message }),
|
||||
{ itemData: { item: i } },
|
||||
);
|
||||
returnData.push(...executionErrorData);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (resource === 'file' && operation === 'download') {
|
||||
// For file downloads the files get attached to the existing items
|
||||
return [items];
|
||||
} else {
|
||||
return [returnData];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.boxTrigger",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": ["Data & Storage"],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/credentials/box/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.boxtrigger/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
import {
|
||||
type IHookFunctions,
|
||||
type IWebhookFunctions,
|
||||
type INodeType,
|
||||
type INodeTypeDescription,
|
||||
type IWebhookResponseData,
|
||||
NodeConnectionTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { boxApiRequest, boxApiRequestAllItems } from './GenericFunctions';
|
||||
|
||||
export class BoxTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Box Trigger',
|
||||
name: 'boxTrigger',
|
||||
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
|
||||
icon: 'file:box.png',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Starts the workflow when Box events occur',
|
||||
defaults: {
|
||||
name: 'Box Trigger',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [
|
||||
{
|
||||
name: 'boxOAuth2Api',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Events',
|
||||
name: 'events',
|
||||
type: 'multiOptions',
|
||||
options: [
|
||||
{
|
||||
name: 'Collaboration Accepted',
|
||||
value: 'COLLABORATION.ACCEPTED',
|
||||
description: 'A collaboration has been accepted',
|
||||
},
|
||||
{
|
||||
name: 'Collaboration Created',
|
||||
value: 'COLLABORATION.CREATED',
|
||||
description: 'A collaboration is created',
|
||||
},
|
||||
{
|
||||
name: 'Collaboration Rejected',
|
||||
value: 'COLLABORATION.REJECTED',
|
||||
description: 'A collaboration has been rejected',
|
||||
},
|
||||
{
|
||||
name: 'Collaboration Removed',
|
||||
value: 'COLLABORATION.REMOVED',
|
||||
description: 'A collaboration has been removed',
|
||||
},
|
||||
{
|
||||
name: 'Collaboration Updated',
|
||||
value: 'COLLABORATION.UPDATED',
|
||||
description: 'A collaboration has been updated',
|
||||
},
|
||||
{
|
||||
name: 'Comment Created',
|
||||
value: 'COMMENT.CREATED',
|
||||
description: 'A comment object is created',
|
||||
},
|
||||
{
|
||||
name: 'Comment Deleted',
|
||||
value: 'COMMENT.DELETED',
|
||||
description: 'A comment object is removed',
|
||||
},
|
||||
{
|
||||
name: 'Comment Updated',
|
||||
value: 'COMMENT.UPDATED',
|
||||
description: 'A comment object is edited',
|
||||
},
|
||||
{
|
||||
name: 'File Copied',
|
||||
value: 'FILE.COPIED',
|
||||
description: 'A file is copied',
|
||||
},
|
||||
{
|
||||
name: 'File Deleted',
|
||||
value: 'FILE.DELETED',
|
||||
description: 'A file is moved to the trash',
|
||||
},
|
||||
{
|
||||
name: 'File Downloaded',
|
||||
value: 'FILE.DOWNLOADED',
|
||||
description: 'A file is downloaded',
|
||||
},
|
||||
{
|
||||
name: 'File Locked',
|
||||
value: 'FILE.LOCKED',
|
||||
description: 'A file is locked',
|
||||
},
|
||||
{
|
||||
name: 'File Moved',
|
||||
value: 'FILE.MOVED',
|
||||
description: 'A file is moved from one folder to another',
|
||||
},
|
||||
{
|
||||
name: 'File Previewed',
|
||||
value: 'FILE.PREVIEWED',
|
||||
description: 'A file is previewed',
|
||||
},
|
||||
{
|
||||
name: 'File Renamed',
|
||||
value: 'FILE.RENAMED',
|
||||
description: 'A file was renamed',
|
||||
},
|
||||
{
|
||||
name: 'File Restored',
|
||||
value: 'FILE.RESTORED',
|
||||
description: 'A file is restored from the trash',
|
||||
},
|
||||
{
|
||||
name: 'File Trashed',
|
||||
value: 'FILE.TRASHED',
|
||||
description: 'A file is moved to the trash',
|
||||
},
|
||||
{
|
||||
name: 'File Unlocked',
|
||||
value: 'FILE.UNLOCKED',
|
||||
description: 'A file is unlocked',
|
||||
},
|
||||
{
|
||||
name: 'File Uploaded',
|
||||
value: 'FILE.UPLOADED',
|
||||
description: 'A file is uploaded to or moved to this folder',
|
||||
},
|
||||
{
|
||||
name: 'Folder Copied',
|
||||
value: 'FOLDER.COPIED',
|
||||
description: 'A copy of a folder is made',
|
||||
},
|
||||
{
|
||||
name: 'Folder Created',
|
||||
value: 'FOLDER.CREATED',
|
||||
description: 'A folder is created',
|
||||
},
|
||||
{
|
||||
name: 'Folder Deleted',
|
||||
value: 'FOLDER.DELETED',
|
||||
description: 'A folder is permanently removed',
|
||||
},
|
||||
{
|
||||
name: 'Folder Downloaded',
|
||||
value: 'FOLDER.DOWNLOADED',
|
||||
description: 'A folder is downloaded',
|
||||
},
|
||||
{
|
||||
name: 'Folder Moved',
|
||||
value: 'FOLDER.MOVED',
|
||||
description: 'A folder is moved to a different folder',
|
||||
},
|
||||
{
|
||||
name: 'Folder Renamed',
|
||||
value: 'FOLDER.RENAMED',
|
||||
description: 'A folder was renamed',
|
||||
},
|
||||
{
|
||||
name: 'Folder Restored',
|
||||
value: 'FOLDER.RESTORED',
|
||||
description: 'A folder is restored from the trash',
|
||||
},
|
||||
{
|
||||
name: 'Folder Trashed',
|
||||
value: 'FOLDER.TRASHED',
|
||||
description: 'A folder is moved to the trash',
|
||||
},
|
||||
{
|
||||
name: 'Metadata Instance Created',
|
||||
value: 'METADATA_INSTANCE.CREATED',
|
||||
description: 'A new metadata template instance is associated with a file or folder',
|
||||
},
|
||||
{
|
||||
name: 'Metadata Instance Deleted',
|
||||
value: 'METADATA_INSTANCE.DELETED',
|
||||
description:
|
||||
'An existing metadata template instance associated with a file or folder is deleted',
|
||||
},
|
||||
{
|
||||
name: 'Metadata Instance Updated',
|
||||
value: 'METADATA_INSTANCE.UPDATED',
|
||||
description:
|
||||
'An attribute (value) is updated/deleted for an existing metadata template instance associated with a file or folder',
|
||||
},
|
||||
{
|
||||
name: 'Sharedlink Created',
|
||||
value: 'SHARED_LINK.CREATED',
|
||||
description: 'A shared link was created',
|
||||
},
|
||||
{
|
||||
name: 'Sharedlink Deleted',
|
||||
value: 'SHARED_LINK.DELETED',
|
||||
description: 'A shared link was deleted',
|
||||
},
|
||||
{
|
||||
name: 'Sharedlink Updated',
|
||||
value: 'SHARED_LINK.UPDATED',
|
||||
description: 'A shared link was updated',
|
||||
},
|
||||
{
|
||||
name: 'Task Assignment Created',
|
||||
value: 'TASK_ASSIGNMENT.CREATED',
|
||||
description: 'A task is created',
|
||||
},
|
||||
{
|
||||
name: 'Task Assignment Updated',
|
||||
value: 'TASK_ASSIGNMENT.UPDATED',
|
||||
description: 'A task is updated',
|
||||
},
|
||||
{
|
||||
name: 'Webhook Deleted',
|
||||
value: 'WEBHOOK.DELETED',
|
||||
description: 'When a webhook is deleted',
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'The events to listen to',
|
||||
},
|
||||
{
|
||||
displayName: 'Target Type',
|
||||
name: 'targetType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'File',
|
||||
value: 'file',
|
||||
},
|
||||
{
|
||||
name: 'Folder',
|
||||
value: 'folder',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'The type of item to trigger a webhook',
|
||||
},
|
||||
{
|
||||
displayName: 'Target ID',
|
||||
name: 'targetId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The ID of the item to trigger a webhook',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const events = this.getNodeParameter('events') as string;
|
||||
const targetId = this.getNodeParameter('targetId') as string;
|
||||
const targetType = this.getNodeParameter('targetType') as string;
|
||||
// Check all the webhooks which exist already if it is identical to the
|
||||
// one that is supposed to get created.
|
||||
const endpoint = '/webhooks';
|
||||
const webhooks = await boxApiRequestAllItems.call(this, 'entries', 'GET', endpoint, {});
|
||||
|
||||
for (const webhook of webhooks) {
|
||||
if (
|
||||
webhook.address === webhookUrl &&
|
||||
webhook.target.id === targetId &&
|
||||
webhook.target.type === targetType
|
||||
) {
|
||||
for (const event of events) {
|
||||
if (!webhook.triggers.includes(event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Found matching webhook, store its ID
|
||||
webhookData.webhookId = webhook.id as string;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||
const events = this.getNodeParameter('events') as string;
|
||||
const targetId = this.getNodeParameter('targetId') as string;
|
||||
const targetType = this.getNodeParameter('targetType') as string;
|
||||
|
||||
const endpoint = '/webhooks';
|
||||
|
||||
const body = {
|
||||
address: webhookUrl,
|
||||
triggers: events,
|
||||
target: {
|
||||
id: targetId,
|
||||
type: targetType,
|
||||
},
|
||||
};
|
||||
|
||||
const responseData = await boxApiRequest.call(this, 'POST', endpoint, body);
|
||||
|
||||
if (responseData.id === undefined) {
|
||||
// Required data is missing so was not successful
|
||||
return false;
|
||||
}
|
||||
|
||||
webhookData.webhookId = responseData.id as string;
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
if (webhookData.webhookId !== undefined) {
|
||||
const endpoint = `/webhooks/${webhookData.webhookId}`;
|
||||
|
||||
try {
|
||||
await boxApiRequest.call(this, 'DELETE', endpoint);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove from the static workflow data so that it is clear
|
||||
// that no webhooks are registered anymore
|
||||
delete webhookData.webhookId;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const bodyData = this.getBodyData();
|
||||
|
||||
return {
|
||||
workflowData: [this.helpers.returnJsonArray(bodyData)],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,731 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const fileOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Copy',
|
||||
value: 'copy',
|
||||
description: 'Copy a file',
|
||||
action: 'Copy a file',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a file',
|
||||
action: 'Delete a file',
|
||||
},
|
||||
{
|
||||
name: 'Download',
|
||||
value: 'download',
|
||||
description: 'Download a file',
|
||||
action: 'Download a file',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a file',
|
||||
action: 'Get a file',
|
||||
},
|
||||
{
|
||||
name: 'Search',
|
||||
value: 'search',
|
||||
description: 'Search files',
|
||||
action: 'Search a file',
|
||||
},
|
||||
{
|
||||
name: 'Share',
|
||||
value: 'share',
|
||||
description: 'Share a file',
|
||||
action: 'Share a file',
|
||||
},
|
||||
{
|
||||
name: 'Upload',
|
||||
value: 'upload',
|
||||
description: 'Upload a file',
|
||||
action: 'Upload a file',
|
||||
},
|
||||
],
|
||||
default: 'upload',
|
||||
},
|
||||
];
|
||||
|
||||
export const fileFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:copy */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['copy'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Parent ID',
|
||||
name: 'parentId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['copy'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
description:
|
||||
'The ID of folder to copy the file to. If not defined will be copied to the root folder.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['copy'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
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: 'Name',
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An optional new name for the copied file',
|
||||
},
|
||||
{
|
||||
displayName: 'Version',
|
||||
name: 'version',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An optional ID of the specific file version to copy',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['delete'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Field ID',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:download */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['download'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Put Output File in Field',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: 'data',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['download'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
hint: 'The name of the output binary field to put the file in',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'File ID',
|
||||
name: 'fileId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['get'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Field ID',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['get'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
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.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:search */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Query',
|
||||
name: 'query',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['search'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'The string to search for. This query is matched against item names, descriptions, text content of files, and various other fields of the different item types.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['search'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['search'],
|
||||
resource: ['file'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 500,
|
||||
},
|
||||
default: 100,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['search'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Content Types',
|
||||
name: 'contet_types',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'Limits search results to items with the given content types. Content types are defined as a comma-separated lists of Box recognized content types.',
|
||||
},
|
||||
{
|
||||
displayName: 'Created At Range',
|
||||
name: 'createdRangeUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
placeholder: 'Add Range',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Range',
|
||||
name: 'createdRangeValuesUi',
|
||||
values: [
|
||||
{
|
||||
displayName: 'From',
|
||||
name: 'from',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'to',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Direction',
|
||||
name: 'direction',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'ASC',
|
||||
value: 'ASC',
|
||||
},
|
||||
{
|
||||
name: 'DESC',
|
||||
value: 'DESC',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description:
|
||||
'Defines the direction in which search results are ordered. Default value is DESC.',
|
||||
},
|
||||
{
|
||||
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: 'File Extensions',
|
||||
name: 'file_extensions',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'pdf,png,gif',
|
||||
description: 'Limits search results to a comma-separated list of file extensions',
|
||||
},
|
||||
{
|
||||
displayName: 'Folder IDs',
|
||||
name: 'ancestor_folder_ids',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'Limits search results to items within the given list of folders. Folders are defined as a comma-separated lists of folder IDs.',
|
||||
},
|
||||
{
|
||||
displayName: 'Scope',
|
||||
name: 'scope',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'User Content',
|
||||
value: 'user_content',
|
||||
},
|
||||
{
|
||||
name: 'Enterprise Content',
|
||||
value: 'enterprise_content',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'Limits search results to a user scope',
|
||||
},
|
||||
{
|
||||
displayName: 'Size Range',
|
||||
name: 'size_range',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '1000000,5000000',
|
||||
description:
|
||||
'Limits search results to items within a given file size range. File size ranges are defined as comma-separated byte sizes.',
|
||||
},
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sort',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Relevance',
|
||||
value: 'relevance',
|
||||
},
|
||||
{
|
||||
name: 'Modified At',
|
||||
value: 'modified_at',
|
||||
},
|
||||
],
|
||||
default: 'relevance',
|
||||
description:
|
||||
'Returns the results ordered in descending order by date at which the item was last modified',
|
||||
},
|
||||
{
|
||||
displayName: 'Trash Content',
|
||||
name: 'trash_content',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Non Trashed Only',
|
||||
value: 'non_trashed_only',
|
||||
},
|
||||
{
|
||||
name: 'Trashed Only',
|
||||
value: 'trashed_only',
|
||||
},
|
||||
],
|
||||
default: 'non_trashed_only',
|
||||
description: 'Controls if search results include the trash',
|
||||
},
|
||||
{
|
||||
displayName: 'Update At Range',
|
||||
name: 'updatedRangeUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
placeholder: 'Add Range',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Range',
|
||||
name: 'updatedRangeValuesUi',
|
||||
values: [
|
||||
{
|
||||
displayName: 'From',
|
||||
name: 'from',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'to',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'User IDs',
|
||||
name: 'owner_user_ids',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'Limits search results to items owned by the given list of owners. Owners are defined as a comma-separated list of user IDs.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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',
|
||||
placeholder: 'name@email.com',
|
||||
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 */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'File Name',
|
||||
name: 'fileName',
|
||||
type: 'string',
|
||||
placeholder: 'photo.png',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The name the file should be saved as',
|
||||
},
|
||||
{
|
||||
displayName: 'Binary File',
|
||||
name: 'binaryData',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
description: 'Whether the data to upload should be taken from binary field',
|
||||
},
|
||||
{
|
||||
displayName: 'File Content',
|
||||
name: 'fileContent',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
binaryData: [false],
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
description: 'The text content of the file',
|
||||
},
|
||||
{
|
||||
displayName: 'Input Binary Field',
|
||||
name: 'binaryPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
binaryData: [true],
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
hint: 'The name of the input binary field containing the file to be uploaded',
|
||||
},
|
||||
{
|
||||
displayName: 'Parent ID',
|
||||
name: 'parentId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['upload'],
|
||||
resource: ['file'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'ID of the parent folder that will contain the file. If not it will be uploaded to the root folder.',
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,790 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const folderOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a folder',
|
||||
action: 'Create a folder',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a folder',
|
||||
action: 'Delete a folder',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a folder',
|
||||
action: 'Get a folder',
|
||||
},
|
||||
{
|
||||
name: 'Search',
|
||||
value: 'search',
|
||||
description: 'Search files',
|
||||
action: 'Search a folder',
|
||||
},
|
||||
{
|
||||
name: 'Share',
|
||||
value: 'share',
|
||||
description: 'Share a folder',
|
||||
action: 'Share a folder',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update folder',
|
||||
action: 'Update a folder',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
},
|
||||
];
|
||||
|
||||
export const folderFields: INodeProperties[] = [
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* folder:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Name',
|
||||
name: 'name',
|
||||
required: true,
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['create'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: "Folder's name",
|
||||
},
|
||||
{
|
||||
displayName: 'Parent ID',
|
||||
name: 'parentId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['create'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'ID of the folder you want to create the new folder in. if not defined it will be created on the root folder.',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['create'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Access',
|
||||
name: 'access',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Collaborators',
|
||||
value: 'collaborators',
|
||||
description:
|
||||
'Only emails from registered email addresses for collaborators will be accepted',
|
||||
},
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
description: 'It will accept emails from any email addres',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'ID of the folder you want to create the new folder in',
|
||||
},
|
||||
{
|
||||
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.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* folder:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Folder ID',
|
||||
name: 'folderId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['get'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* folder:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Folder ID',
|
||||
name: 'folderId',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['delete'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Recursive',
|
||||
name: 'recursive',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['delete'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description:
|
||||
'Whether to delete a folder that is not empty by recursively deleting the folder and all of its content',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* file:search */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Query',
|
||||
name: 'query',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['search'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description:
|
||||
'The string to search for. This query is matched against item names, descriptions, text content of files, and various other fields of the different item types.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['search'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['search'],
|
||||
resource: ['folder'],
|
||||
returnAll: [false],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 500,
|
||||
},
|
||||
default: 100,
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: ['search'],
|
||||
resource: ['folder'],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Content Types',
|
||||
name: 'contet_types',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'Limits search results to items with the given content types. Content types are defined as a comma-separated lists of Box recognized content types.',
|
||||
},
|
||||
{
|
||||
displayName: 'Created At Range',
|
||||
name: 'createdRangeUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
placeholder: 'Add Range',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Range',
|
||||
name: 'createdRangeValuesUi',
|
||||
values: [
|
||||
{
|
||||
displayName: 'From',
|
||||
name: 'from',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'to',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Direction',
|
||||
name: 'direction',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'ASC',
|
||||
value: 'ASC',
|
||||
},
|
||||
{
|
||||
name: 'DESC',
|
||||
value: 'DESC',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description:
|
||||
'Defines the direction in which search results are ordered. Default value is DESC.',
|
||||
},
|
||||
{
|
||||
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: 'File Extensions',
|
||||
name: 'file_extensions',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'pdf,png,gif',
|
||||
description: 'Limits search results to a comma-separated list of file extensions',
|
||||
},
|
||||
{
|
||||
displayName: 'Folder IDs',
|
||||
name: 'ancestor_folder_ids',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'Limits search results to items within the given list of folders. Folders are defined as a comma-separated lists of folder IDs.',
|
||||
},
|
||||
{
|
||||
displayName: 'Scope',
|
||||
name: 'scope',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'User Content',
|
||||
value: 'user_content',
|
||||
},
|
||||
{
|
||||
name: 'Enterprise Content',
|
||||
value: 'enterprise_content',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'Limits search results to a user scope',
|
||||
},
|
||||
{
|
||||
displayName: 'Size Range',
|
||||
name: 'size_range',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '1000000,5000000',
|
||||
description:
|
||||
'Limits search results to items within a given file size range. File size ranges are defined as comma-separated byte sizes.',
|
||||
},
|
||||
{
|
||||
displayName: 'Sort',
|
||||
name: 'sort',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Relevance',
|
||||
value: 'relevance',
|
||||
},
|
||||
{
|
||||
name: 'Modified At',
|
||||
value: 'modified_at',
|
||||
},
|
||||
],
|
||||
default: 'relevance',
|
||||
description:
|
||||
'Returns the results ordered in descending order by date at which the item was last modified',
|
||||
},
|
||||
{
|
||||
displayName: 'Trash Content',
|
||||
name: 'trash_content',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Non Trashed Only',
|
||||
value: 'non_trashed_only',
|
||||
},
|
||||
{
|
||||
name: 'Trashed Only',
|
||||
value: 'trashed_only',
|
||||
},
|
||||
],
|
||||
default: 'non_trashed_only',
|
||||
description: 'Controls if search results include the trash',
|
||||
},
|
||||
{
|
||||
displayName: 'Update At Range',
|
||||
name: 'updatedRangeUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: false,
|
||||
},
|
||||
placeholder: 'Add Range',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Range',
|
||||
name: 'updatedRangeValuesUi',
|
||||
values: [
|
||||
{
|
||||
displayName: 'From',
|
||||
name: 'from',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'To',
|
||||
name: 'to',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'User IDs',
|
||||
name: 'owner_user_ids',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'Limits search results to items owned by the given list of owners. Owners are defined as a comma-separated list of user IDs.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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',
|
||||
placeholder: 'name@email.com',
|
||||
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: '',
|
||||
},
|
||||
{
|
||||
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:
|
||||
'Whether 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:
|
||||
'Whether to restrict 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:
|
||||
'Whether 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',
|
||||
typeOptions: { password: true },
|
||||
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: 'Whether 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.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,71 @@
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
IHookFunctions,
|
||||
IHttpRequestMethods,
|
||||
ILoadOptionsFunctions,
|
||||
IOAuth2Options,
|
||||
IRequestOptions,
|
||||
JsonObject,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeApiError } from 'n8n-workflow';
|
||||
|
||||
export async function boxApiRequest(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions,
|
||||
method: IHttpRequestMethods,
|
||||
resource: string,
|
||||
|
||||
body: any = {},
|
||||
qs: IDataObject = {},
|
||||
uri?: string,
|
||||
option: IDataObject = {},
|
||||
): Promise<any> {
|
||||
let options: IRequestOptions = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: uri || `https://api.box.com/2.0${resource}`,
|
||||
json: true,
|
||||
};
|
||||
options = Object.assign({}, options, option);
|
||||
|
||||
try {
|
||||
if (Object.keys(body as IDataObject).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
const oAuth2Options: IOAuth2Options = {
|
||||
includeCredentialsOnRefreshOnBody: true,
|
||||
};
|
||||
|
||||
return await this.helpers.requestOAuth2.call(this, 'boxOAuth2Api', options, oAuth2Options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
export async function boxApiRequestAllItems(
|
||||
this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions,
|
||||
propertyName: string,
|
||||
method: IHttpRequestMethods,
|
||||
endpoint: string,
|
||||
|
||||
body: any = {},
|
||||
query: IDataObject = {},
|
||||
): Promise<any> {
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
let responseData;
|
||||
query.limit = 100;
|
||||
query.offset = 0;
|
||||
do {
|
||||
responseData = await boxApiRequest.call(this, method, endpoint, body, query);
|
||||
query.offset = (responseData.offset as number) + query.limit;
|
||||
returnData.push.apply(returnData, responseData[propertyName] as IDataObject[]);
|
||||
} while (responseData[propertyName].length !== 0);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content_created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"file_version": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"item_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owned_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"purged_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"shared_link": {
|
||||
"type": "null"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trashed_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 5
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content_created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"file_version": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"item_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owned_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"purged_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trashed_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content_created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"file_version": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"folder_upload_email": {
|
||||
"type": "null"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"item_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owned_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"purged_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trashed_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 2
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content_created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"file_version": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"item_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owned_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"purged_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"shared_link": {
|
||||
"type": "null"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trashed_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content_created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"folder_upload_email": {
|
||||
"type": "null"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"item_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"offset": {
|
||||
"type": "integer"
|
||||
},
|
||||
"order": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"direction": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"item_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owned_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"purged_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"shared_link": {
|
||||
"type": "null"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trashed_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content_created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"item_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"file_version": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"limit": {
|
||||
"type": "integer"
|
||||
},
|
||||
"offset": {
|
||||
"type": "integer"
|
||||
},
|
||||
"order": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"by": {
|
||||
"type": "string"
|
||||
},
|
||||
"direction": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"item_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owned_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"purged_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trashed_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content_created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"content_modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"created_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"etag": {
|
||||
"type": "string"
|
||||
},
|
||||
"folder_upload_email": {
|
||||
"type": "null"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"item_status": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"modified_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"owned_by": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"login": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path_collection": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_count": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"purged_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"sequence_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"trashed_at": {
|
||||
"type": "null"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import type { IHookFunctions } from 'n8n-workflow';
|
||||
import { BoxTrigger } from '../BoxTrigger.node';
|
||||
|
||||
describe('Box Trigger Webhook Lifecycle', () => {
|
||||
const mockHookFunctions = mock<IHookFunctions>();
|
||||
const mockStaticData: Record<string, string> = {};
|
||||
const mockRequestOAuth2 = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
Object.keys(mockStaticData).forEach((key) => delete mockStaticData[key]);
|
||||
mockHookFunctions.getWorkflowStaticData.mockReturnValue(mockStaticData);
|
||||
mockHookFunctions.getNodeWebhookUrl.mockReturnValue('https://n8n.io/webhook/box-test');
|
||||
|
||||
// Mock getNodeParameter to return different values based on parameter name
|
||||
mockHookFunctions.getNodeParameter.mockImplementation((parameterName: string) => {
|
||||
switch (parameterName) {
|
||||
case 'events':
|
||||
return ['FILE.UPLOADED'];
|
||||
case 'targetId':
|
||||
return '12345';
|
||||
case 'targetType':
|
||||
return 'file';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
// Mock the OAuth2 request helper
|
||||
mockHookFunctions.helpers = {
|
||||
...mockHookFunctions.helpers,
|
||||
requestOAuth2: mockRequestOAuth2,
|
||||
};
|
||||
});
|
||||
|
||||
describe('checkExists', () => {
|
||||
it('should not store webhook ID when no matching webhook exists', async () => {
|
||||
// Mock API response with no matching webhooks
|
||||
mockRequestOAuth2
|
||||
.mockResolvedValueOnce({
|
||||
entries: [
|
||||
{
|
||||
id: 'webhook_123',
|
||||
address: 'https://other-app.com/webhook',
|
||||
target: { id: '67890', type: 'file' },
|
||||
triggers: ['FILE.UPLOADED'],
|
||||
},
|
||||
{
|
||||
id: 'webhook_456',
|
||||
address: 'https://n8n.io/webhook/box-test',
|
||||
target: { id: '67890', type: 'file' }, // Different target ID
|
||||
triggers: ['FILE.UPLOADED'],
|
||||
},
|
||||
],
|
||||
offset: 0,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
entries: [], // Empty to terminate the loop
|
||||
offset: 100,
|
||||
});
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const exists = await boxTrigger.webhookMethods.default.checkExists.call(mockHookFunctions);
|
||||
|
||||
expect(exists).toBe(false);
|
||||
expect(mockStaticData.webhookId).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should store the correct webhook ID when a matching webhook exists', async () => {
|
||||
// Mock API response with a matching webhook
|
||||
// boxApiRequestAllItems expects a response with 'entries' property
|
||||
mockRequestOAuth2
|
||||
.mockResolvedValueOnce({
|
||||
entries: [
|
||||
{
|
||||
id: 'webhook_123',
|
||||
address: 'https://other-app.com/webhook',
|
||||
target: { id: '67890', type: 'file' },
|
||||
triggers: ['FILE.UPLOADED'],
|
||||
},
|
||||
{
|
||||
id: 'webhook_456',
|
||||
address: 'https://n8n.io/webhook/box-test',
|
||||
target: { id: '12345', type: 'file' }, // Matching target
|
||||
triggers: ['FILE.UPLOADED'],
|
||||
},
|
||||
],
|
||||
offset: 0,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
entries: [], // Empty to terminate the loop
|
||||
offset: 100,
|
||||
});
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const exists = await boxTrigger.webhookMethods.default.checkExists.call(mockHookFunctions);
|
||||
|
||||
expect(exists).toBe(true);
|
||||
expect(mockStaticData.webhookId).toBe('webhook_456');
|
||||
});
|
||||
|
||||
it('should return false when webhook exists but triggers are missing', async () => {
|
||||
// Mock API response with matching webhook but missing triggers
|
||||
mockRequestOAuth2
|
||||
.mockResolvedValueOnce({
|
||||
entries: [
|
||||
{
|
||||
id: 'webhook_456',
|
||||
address: 'https://n8n.io/webhook/box-test',
|
||||
target: { id: '12345', type: 'file' },
|
||||
triggers: ['FILE.DOWNLOADED'], // Different trigger
|
||||
},
|
||||
],
|
||||
offset: 0,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
entries: [], // Empty to terminate the loop
|
||||
offset: 100,
|
||||
});
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const exists = await boxTrigger.webhookMethods.default.checkExists.call(mockHookFunctions);
|
||||
|
||||
expect(exists).toBe(false);
|
||||
expect(mockStaticData.webhookId).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return false when webhook exists but target type is different', async () => {
|
||||
// Mock API response with matching webhook but different target type
|
||||
mockRequestOAuth2
|
||||
.mockResolvedValueOnce({
|
||||
entries: [
|
||||
{
|
||||
id: 'webhook_456',
|
||||
address: 'https://n8n.io/webhook/box-test',
|
||||
target: { id: '12345', type: 'folder' }, // Different target type
|
||||
triggers: ['FILE.UPLOADED'],
|
||||
},
|
||||
],
|
||||
offset: 0,
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
entries: [], // Empty to terminate the loop
|
||||
offset: 100,
|
||||
});
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const exists = await boxTrigger.webhookMethods.default.checkExists.call(mockHookFunctions);
|
||||
|
||||
expect(exists).toBe(false);
|
||||
expect(mockStaticData.webhookId).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should create a new webhook when none exists', async () => {
|
||||
mockRequestOAuth2.mockResolvedValue({
|
||||
id: 'webhook_new_789',
|
||||
});
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const created = await boxTrigger.webhookMethods.default.create.call(mockHookFunctions);
|
||||
|
||||
expect(created).toBe(true);
|
||||
expect(mockStaticData.webhookId).toBe('webhook_new_789');
|
||||
expect(mockHookFunctions.helpers.requestOAuth2).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
method: 'POST',
|
||||
uri: 'https://api.box.com/2.0/webhooks',
|
||||
body: {
|
||||
address: 'https://n8n.io/webhook/box-test',
|
||||
triggers: ['FILE.UPLOADED'],
|
||||
target: {
|
||||
id: '12345',
|
||||
type: 'file',
|
||||
},
|
||||
},
|
||||
}),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return false when webhook creation fails', async () => {
|
||||
mockRequestOAuth2.mockResolvedValue({
|
||||
// No id in response
|
||||
});
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const created = await boxTrigger.webhookMethods.default.create.call(mockHookFunctions);
|
||||
|
||||
expect(created).toBe(false);
|
||||
expect(mockStaticData.webhookId).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should delete the correct webhook upon deactivation', async () => {
|
||||
mockStaticData.webhookId = 'webhook_456';
|
||||
mockRequestOAuth2.mockResolvedValue({});
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const deleted = await boxTrigger.webhookMethods.default.delete.call(mockHookFunctions);
|
||||
|
||||
expect(deleted).toBe(true);
|
||||
expect(mockHookFunctions.helpers.requestOAuth2).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
method: 'DELETE',
|
||||
uri: 'https://api.box.com/2.0/webhooks/webhook_456',
|
||||
}),
|
||||
expect.anything(),
|
||||
);
|
||||
expect(mockStaticData.webhookId).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle deletion when no webhook ID is stored', async () => {
|
||||
// No webhookId in static data
|
||||
mockRequestOAuth2.mockResolvedValue({});
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const deleted = await boxTrigger.webhookMethods.default.delete.call(mockHookFunctions);
|
||||
|
||||
expect(deleted).toBe(true);
|
||||
expect(mockHookFunctions.helpers.requestOAuth2).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle delete API errors gracefully', async () => {
|
||||
mockStaticData.webhookId = 'webhook_456';
|
||||
mockRequestOAuth2.mockRejectedValue(new Error('API Error'));
|
||||
|
||||
const boxTrigger = new BoxTrigger();
|
||||
const deleted = await boxTrigger.webhookMethods.default.delete.call(mockHookFunctions);
|
||||
|
||||
expect(deleted).toBe(false);
|
||||
expect(mockStaticData.webhookId).toBe('webhook_456'); // Should not be deleted on error
|
||||
});
|
||||
});
|
||||
});
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Reference in New Issue
Block a user