diff --git a/packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts b/packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts
new file mode 100644
index 0000000000..2840deebeb
--- /dev/null
+++ b/packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts
@@ -0,0 +1,164 @@
+import { INodeProperties } from 'n8n-workflow';
+
+export const fileOperations: INodeProperties[] = [
+ {
+ displayName: 'Operation',
+ name: 'operation',
+ type: 'options',
+ noDataExpression: true,
+ displayOptions: {
+ show: {
+ resource: ['file'],
+ },
+ },
+ options: [
+ {
+ name: 'Create',
+ value: 'create',
+ description: 'Create a file',
+ action: 'Create a file',
+ },
+ {
+ name: 'Delete',
+ value: 'delete',
+ description: 'Delete file',
+ action: 'Delete a file',
+ },
+ {
+ name: 'Get',
+ value: 'get',
+ description: 'Get a file content',
+ action: 'Get a file content',
+ },
+ {
+ name: 'Get Many',
+ value: 'getAll',
+ description: 'Get many files',
+ action: 'Get many files',
+ },
+ ],
+ default: 'get',
+ },
+];
+
+export const fileFields: INodeProperties[] = [
+ /* -------------------------------------------------------------------------- */
+ /* file:* */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'Form Name or ID',
+ name: 'formId',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'loadForms',
+ },
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['file'],
+ },
+ },
+ description:
+ 'Form ID (e.g. aSAvYreNzVEkrWg5Gdcvg). Choose from the list, or specify an ID using an expression.',
+ },
+ /* -------------------------------------------------------------------------- */
+ /* file:delete */
+ /* -------------------------------------------------------------------------- */
+ {
+ displayName: 'File ID',
+ name: 'fileId',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['file'],
+ operation: ['delete', 'get'],
+ },
+ },
+ description: 'Uid of the file (should start with "af" e.g. "afQoJxA4kmKEXVpkH6SYbhb"',
+ },
+ {
+ displayName: 'Property Name',
+ name: 'binaryPropertyName',
+ type: 'string',
+ required: true,
+ default: 'data',
+ displayOptions: {
+ show: {
+ resource: ['file'],
+ operation: ['get'],
+ },
+ },
+ description: 'Name of the binary property to write the file into',
+ },
+ {
+ displayName: 'Download File Content',
+ name: 'download',
+ type: 'boolean',
+ required: true,
+ default: false,
+ displayOptions: {
+ show: {
+ resource: ['file'],
+ operation: ['get'],
+ },
+ },
+ description: 'Whether to download the file content into a binary property',
+ },
+ {
+ displayName: 'File Upload Mode',
+ name: 'fileMode',
+ type: 'options',
+ required: true,
+ default: 'binary',
+ displayOptions: {
+ show: {
+ resource: ['file'],
+ operation: ['create'],
+ },
+ },
+ options: [
+ {
+ name: 'Binary Data',
+ value: 'binary',
+ },
+ {
+ name: 'URL',
+ value: 'url',
+ },
+ ],
+ },
+ {
+ displayName: 'Property Name',
+ name: 'binaryPropertyName',
+ type: 'string',
+ required: true,
+ default: 'data',
+ displayOptions: {
+ show: {
+ resource: ['file'],
+ operation: ['create'],
+ fileMode: ['binary'],
+ },
+ },
+ description:
+ 'Name of the binary property containing the file to upload. Supported types: image, audio, video, csv, xml, zip.',
+ },
+ {
+ displayName: 'File URL',
+ name: 'fileUrl',
+ type: 'string',
+ required: true,
+ default: '',
+ displayOptions: {
+ show: {
+ resource: ['file'],
+ operation: ['create'],
+ fileMode: ['url'],
+ },
+ },
+ description: 'HTTP(s) link to the file to upload',
+ },
+];
diff --git a/packages/nodes-base/nodes/KoBoToolbox/FormDescription.ts b/packages/nodes-base/nodes/KoBoToolbox/FormDescription.ts
index 0ce615f7fa..301fea92f1 100644
--- a/packages/nodes-base/nodes/KoBoToolbox/FormDescription.ts
+++ b/packages/nodes-base/nodes/KoBoToolbox/FormDescription.ts
@@ -24,6 +24,12 @@ export const formOperations: INodeProperties[] = [
description: 'Get many forms',
action: 'Get many forms',
},
+ {
+ name: 'Redeploy',
+ value: 'redeploy',
+ description: 'Redeploy Current Form Version',
+ action: 'Redeploy Current Form Version',
+ },
],
default: 'get',
},
@@ -34,18 +40,22 @@ export const formFields: INodeProperties[] = [
/* form:get */
/* -------------------------------------------------------------------------- */
{
- displayName: 'Form ID',
+ displayName: 'Form Name or ID',
name: 'formId',
- type: 'string',
+ type: 'options',
+ typeOptions: {
+ loadOptionsMethod: 'loadForms',
+ },
required: true,
default: '',
displayOptions: {
show: {
resource: ['form'],
- operation: ['get'],
+ operation: ['get', 'redeploy'],
},
},
- description: 'Form ID (e.g. aSAvYreNzVEkrWg5Gdcvg)',
+ description:
+ 'Form ID (e.g. aSAvYreNzVEkrWg5Gdcvg). Choose from the list, or specify an ID using an expression.',
},
/* -------------------------------------------------------------------------- */
/* form:getAll */
diff --git a/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts b/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts
index de39154965..7b8f8c8f7c 100644
--- a/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts
+++ b/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts
@@ -60,6 +60,20 @@ export async function koBoToolboxApiRequest(
return results;
}
+export async function koBoToolboxRawRequest(
+ this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions,
+ option: IHttpRequestOptions,
+ // tslint:disable-next-line:no-any
+): Promise {
+ const credentials = await this.getCredentials('koBoToolboxApi');
+
+ if (option.url && !/^http(s)?:/.test(option.url)) {
+ option.url = credentials.URL + option.url;
+ }
+
+ return await this.helpers.httpRequestWithAuthentication.call(this, 'koBoToolboxApi', option);
+}
+
function parseGeoPoint(geoPoint: string): null | number[] {
// Check if it looks like a "lat lon z precision" flat string e.g. "-1.931161 30.079811 0 0" (lat, lon, elevation, precision)
// NOTE: we are discarding the elevation and precision values since they're not (well) supported in GeoJSON
diff --git a/packages/nodes-base/nodes/KoBoToolbox/HookDescription.ts b/packages/nodes-base/nodes/KoBoToolbox/HookDescription.ts
index 9e35470bd5..855a07b9f4 100644
--- a/packages/nodes-base/nodes/KoBoToolbox/HookDescription.ts
+++ b/packages/nodes-base/nodes/KoBoToolbox/HookDescription.ts
@@ -63,7 +63,7 @@ export const hookFields: INodeProperties[] = [
displayOptions: {
show: {
resource: ['hook'],
- operation: ['get', 'retryOne', 'retryAll', 'getLogs'],
+ operation: ['get', 'retryOne', 'retryAll', 'getLogs', 'getAll'],
},
},
description:
@@ -86,24 +86,6 @@ export const hookFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* hook:getAll */
/* -------------------------------------------------------------------------- */
- {
- displayName: 'Form Name or ID',
- name: 'formId',
- type: 'options',
- typeOptions: {
- loadOptionsMethod: 'loadForms',
- },
- required: true,
- default: '',
- displayOptions: {
- show: {
- resource: ['hook'],
- operation: ['getAll'],
- },
- },
- description:
- 'Form ID (e.g. aSAvYreNzVEkrWg5Gdcvg). Choose from the list, or specify an ID using an expression.',
- },
{
displayName: 'Hook Log ID',
name: 'logId',
diff --git a/packages/nodes-base/nodes/KoBoToolbox/KoBoToolbox.node.ts b/packages/nodes-base/nodes/KoBoToolbox/KoBoToolbox.node.ts
index 1d47179c1a..b9807c3931 100644
--- a/packages/nodes-base/nodes/KoBoToolbox/KoBoToolbox.node.ts
+++ b/packages/nodes-base/nodes/KoBoToolbox/KoBoToolbox.node.ts
@@ -1,11 +1,19 @@
import { IExecuteFunctions } from 'n8n-core';
-import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
+import {
+ IBinaryData,
+ IBinaryKeyData,
+ IDataObject,
+ INodeExecutionData,
+ INodeType,
+ INodeTypeDescription,
+} from 'n8n-workflow';
import {
downloadAttachments,
formatSubmission,
koBoToolboxApiRequest,
+ koBoToolboxRawRequest,
loadForms,
parseStringList,
} from './GenericFunctions';
@@ -16,6 +24,8 @@ import { submissionFields, submissionOperations } from './SubmissionDescription'
import { hookFields, hookOperations } from './HookDescription';
+import { fileFields, fileOperations } from './FileDescription';
+
export class KoBoToolbox implements INodeType {
description: INodeTypeDescription = {
displayName: 'KoBoToolbox',
@@ -43,6 +53,10 @@ export class KoBoToolbox implements INodeType {
type: 'options',
noDataExpression: true,
options: [
+ {
+ name: 'File',
+ value: 'file',
+ },
{
name: 'Form',
value: 'form',
@@ -65,6 +79,8 @@ export class KoBoToolbox implements INodeType {
...hookFields,
...submissionOperations,
...submissionFields,
+ ...fileOperations,
+ ...fileFields,
],
};
@@ -129,7 +145,21 @@ export class KoBoToolbox implements INodeType {
scroll: this.getNodeParameter('returnAll', i),
});
}
+
+ if (operation === 'redeploy') {
+ // ----------------------------------
+ // Form: redeploy
+ // ----------------------------------
+ const formId = this.getNodeParameter('formId', i) as string;
+ responseData = [
+ await koBoToolboxApiRequest.call(this, {
+ method: 'PATCH',
+ url: `/api/v2/assets/${formId}/deployment/`,
+ }),
+ ];
+ }
}
+
if (resource === 'submission') {
// *********************************************************************
// Submissions
@@ -342,6 +372,102 @@ export class KoBoToolbox implements INodeType {
}
}
+ if (resource === 'file') {
+ // *********************************************************************
+ // File
+ // *********************************************************************
+ const formId = this.getNodeParameter('formId', i) as string;
+
+ if (operation === 'getAll') {
+ responseData = [
+ await koBoToolboxApiRequest.call(this, {
+ url: `/api/v2/assets/${formId}/files`,
+ qs: {
+ file_type: 'form_media',
+ },
+ scroll: true,
+ }),
+ ];
+ }
+
+ if (operation === 'get') {
+ const fileId = this.getNodeParameter('fileId', i) as string;
+ const download = this.getNodeParameter('download', i) as boolean;
+
+ responseData = [
+ await koBoToolboxApiRequest.call(this, {
+ url: `/api/v2/assets/${formId}/files/${fileId}`,
+ }),
+ ];
+
+ if (responseData && responseData[0] && download) {
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;
+
+ const binaryItem: INodeExecutionData = {
+ json: responseData[0],
+ binary: {},
+ };
+
+ const response = await koBoToolboxRawRequest.call(this, {
+ url: `/api/v2/assets/${formId}/files/${fileId}/content`,
+ encoding: 'arraybuffer',
+ });
+
+ console.dir(response);
+
+ binaryItem.binary![binaryPropertyName] = await this.helpers.prepareBinaryData(
+ response,
+ responseData[0].metadata.filename,
+ );
+
+ binaryItems.push(binaryItem);
+ }
+ }
+
+ if (operation === 'delete') {
+ const fileId = this.getNodeParameter('fileId', i) as string;
+ responseData = [
+ await koBoToolboxApiRequest.call(this, {
+ method: 'DELETE',
+ url: `/api/v2/assets/${formId}/files/${fileId}`,
+ }),
+ ];
+ }
+
+ if (operation === 'create') {
+ const fileMode = this.getNodeParameter('fileMode', i) as string;
+ const body: IDataObject = {
+ description: 'Uploaded file',
+ file_type: 'form_media',
+ };
+
+ if ('binary' === fileMode) {
+ const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;
+ const item = items[i].binary as IBinaryKeyData;
+ const binaryData = item[binaryPropertyName] as IBinaryData;
+
+ body.base64Encoded = 'data:' + binaryData.mimeType + ';base64,' + binaryData.data;
+ body.metadata = {
+ filename: binaryData.fileName,
+ };
+ } else {
+ const fileUrl = this.getNodeParameter('fileUrl', i) as string;
+
+ body.metadata = {
+ redirect_url: fileUrl,
+ };
+ }
+
+ responseData = [
+ await koBoToolboxApiRequest.call(this, {
+ method: 'POST',
+ url: `/api/v2/assets/${formId}/files/`,
+ body,
+ }),
+ ];
+ }
+ }
+
returnData = returnData.concat(responseData);
}
diff --git a/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts b/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts
index 876498f718..0ad35b3180 100644
--- a/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts
+++ b/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts
@@ -74,7 +74,6 @@ export class KoBoToolboxTrigger implements INodeType {
],
};
- // @ts-ignore
webhookMethods = {
default: {
async checkExists(this: IHookFunctions): Promise {
diff --git a/packages/nodes-base/nodes/KoBoToolbox/SubmissionDescription.ts b/packages/nodes-base/nodes/KoBoToolbox/SubmissionDescription.ts
index c6809c67f0..5b5a19c578 100644
--- a/packages/nodes-base/nodes/KoBoToolbox/SubmissionDescription.ts
+++ b/packages/nodes-base/nodes/KoBoToolbox/SubmissionDescription.ts
@@ -63,7 +63,7 @@ export const submissionFields: INodeProperties[] = [
displayOptions: {
show: {
resource: ['submission'],
- operation: ['get', 'delete', 'getValidation', 'setValidation'],
+ operation: ['get', 'delete', 'getValidation', 'setValidation', 'getAll'],
},
},
description:
@@ -114,24 +114,6 @@ export const submissionFields: INodeProperties[] = [
/* -------------------------------------------------------------------------- */
/* submission:getAll */
/* -------------------------------------------------------------------------- */
- {
- displayName: 'Form Name or ID',
- name: 'formId',
- type: 'options',
- typeOptions: {
- loadOptionsMethod: 'loadForms',
- },
- required: true,
- default: '',
- displayOptions: {
- show: {
- resource: ['submission'],
- operation: ['getAll'],
- },
- },
- description:
- 'Form ID (e.g. aSAvYreNzVEkrWg5Gdcvg). Choose from the list, or specify an ID using an expression.',
- },
{
displayName: 'Return All',
name: 'returnAll',