mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(KoBoToolbox Node): Add support for Media file API (#4578)
This commit is contained in:
parent
c3114241fd
commit
37e580eb06
164
packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts
Normal file
164
packages/nodes-base/nodes/KoBoToolbox/FileDescription.ts
Normal file
|
@ -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 <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
||||||
|
},
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* 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',
|
||||||
|
},
|
||||||
|
];
|
|
@ -24,6 +24,12 @@ export const formOperations: INodeProperties[] = [
|
||||||
description: 'Get many forms',
|
description: 'Get many forms',
|
||||||
action: 'Get many forms',
|
action: 'Get many forms',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Redeploy',
|
||||||
|
value: 'redeploy',
|
||||||
|
description: 'Redeploy Current Form Version',
|
||||||
|
action: 'Redeploy Current Form Version',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
default: 'get',
|
default: 'get',
|
||||||
},
|
},
|
||||||
|
@ -34,18 +40,22 @@ export const formFields: INodeProperties[] = [
|
||||||
/* form:get */
|
/* form:get */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
{
|
{
|
||||||
displayName: 'Form ID',
|
displayName: 'Form Name or ID',
|
||||||
name: 'formId',
|
name: 'formId',
|
||||||
type: 'string',
|
type: 'options',
|
||||||
|
typeOptions: {
|
||||||
|
loadOptionsMethod: 'loadForms',
|
||||||
|
},
|
||||||
required: true,
|
required: true,
|
||||||
default: '',
|
default: '',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: ['form'],
|
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 <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
||||||
},
|
},
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* form:getAll */
|
/* form:getAll */
|
||||||
|
|
|
@ -60,6 +60,20 @@ export async function koBoToolboxApiRequest(
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function koBoToolboxRawRequest(
|
||||||
|
this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions,
|
||||||
|
option: IHttpRequestOptions,
|
||||||
|
// tslint:disable-next-line:no-any
|
||||||
|
): Promise<any> {
|
||||||
|
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[] {
|
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)
|
// 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
|
// NOTE: we are discarding the elevation and precision values since they're not (well) supported in GeoJSON
|
||||||
|
|
|
@ -63,7 +63,7 @@ export const hookFields: INodeProperties[] = [
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: ['hook'],
|
resource: ['hook'],
|
||||||
operation: ['get', 'retryOne', 'retryAll', 'getLogs'],
|
operation: ['get', 'retryOne', 'retryAll', 'getLogs', 'getAll'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
|
@ -86,24 +86,6 @@ export const hookFields: INodeProperties[] = [
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* hook:getAll */
|
/* 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 <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
displayName: 'Hook Log ID',
|
displayName: 'Hook Log ID',
|
||||||
name: 'logId',
|
name: 'logId',
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
import { IExecuteFunctions } from 'n8n-core';
|
import { IExecuteFunctions } from 'n8n-core';
|
||||||
|
|
||||||
import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
import {
|
||||||
|
IBinaryData,
|
||||||
|
IBinaryKeyData,
|
||||||
|
IDataObject,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
downloadAttachments,
|
downloadAttachments,
|
||||||
formatSubmission,
|
formatSubmission,
|
||||||
koBoToolboxApiRequest,
|
koBoToolboxApiRequest,
|
||||||
|
koBoToolboxRawRequest,
|
||||||
loadForms,
|
loadForms,
|
||||||
parseStringList,
|
parseStringList,
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
@ -16,6 +24,8 @@ import { submissionFields, submissionOperations } from './SubmissionDescription'
|
||||||
|
|
||||||
import { hookFields, hookOperations } from './HookDescription';
|
import { hookFields, hookOperations } from './HookDescription';
|
||||||
|
|
||||||
|
import { fileFields, fileOperations } from './FileDescription';
|
||||||
|
|
||||||
export class KoBoToolbox implements INodeType {
|
export class KoBoToolbox implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'KoBoToolbox',
|
displayName: 'KoBoToolbox',
|
||||||
|
@ -43,6 +53,10 @@ export class KoBoToolbox implements INodeType {
|
||||||
type: 'options',
|
type: 'options',
|
||||||
noDataExpression: true,
|
noDataExpression: true,
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
name: 'File',
|
||||||
|
value: 'file',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Form',
|
name: 'Form',
|
||||||
value: 'form',
|
value: 'form',
|
||||||
|
@ -65,6 +79,8 @@ export class KoBoToolbox implements INodeType {
|
||||||
...hookFields,
|
...hookFields,
|
||||||
...submissionOperations,
|
...submissionOperations,
|
||||||
...submissionFields,
|
...submissionFields,
|
||||||
|
...fileOperations,
|
||||||
|
...fileFields,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -129,7 +145,21 @@ export class KoBoToolbox implements INodeType {
|
||||||
scroll: this.getNodeParameter('returnAll', i),
|
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') {
|
if (resource === 'submission') {
|
||||||
// *********************************************************************
|
// *********************************************************************
|
||||||
// Submissions
|
// 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);
|
returnData = returnData.concat(responseData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,6 @@ export class KoBoToolboxTrigger implements INodeType {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
webhookMethods = {
|
webhookMethods = {
|
||||||
default: {
|
default: {
|
||||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||||
|
|
|
@ -63,7 +63,7 @@ export const submissionFields: INodeProperties[] = [
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: ['submission'],
|
resource: ['submission'],
|
||||||
operation: ['get', 'delete', 'getValidation', 'setValidation'],
|
operation: ['get', 'delete', 'getValidation', 'setValidation', 'getAll'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
description:
|
description:
|
||||||
|
@ -114,24 +114,6 @@ export const submissionFields: INodeProperties[] = [
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* submission:getAll */
|
/* 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 <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
displayName: 'Return All',
|
displayName: 'Return All',
|
||||||
name: 'returnAll',
|
name: 'returnAll',
|
||||||
|
|
Loading…
Reference in a new issue