n8n/packages/nodes-base/nodes/KoBoToolbox/KoBoToolbox.node.ts

478 lines
14 KiB
TypeScript
Raw Normal View History

import { IExecuteFunctions } from 'n8n-core';
import {
IBinaryData,
IBinaryKeyData,
IDataObject,
INodeExecutionData,
INodeType,
INodeTypeDescription,
} from 'n8n-workflow';
import {
downloadAttachments,
formatSubmission,
koBoToolboxApiRequest,
koBoToolboxRawRequest,
loadForms,
parseStringList,
} from './GenericFunctions';
import { formFields, formOperations } from './FormDescription';
import { submissionFields, submissionOperations } from './SubmissionDescription';
import { hookFields, hookOperations } from './HookDescription';
import { fileFields, fileOperations } from './FileDescription';
export class KoBoToolbox implements INodeType {
description: INodeTypeDescription = {
displayName: 'KoBoToolbox',
name: 'koBoToolbox',
icon: 'file:koBoToolbox.svg',
group: ['transform'],
version: 1,
description: 'Work with KoBoToolbox forms and submissions',
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
defaults: {
name: 'KoBoToolbox',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'koBoToolboxApi',
required: true,
},
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
refactor: Apply more nodelinting rules (#3324) * :pencil2: Alphabetize lint rules * :fire: Remove duplicates * :zap: Update `lintfix` script * :shirt: Apply `node-param-operation-without-no-data-expression` (#3329) * :shirt: Apply `node-param-operation-without-no-data-expression` * :shirt: Add exceptions * :shirt: Apply `node-param-description-weak` (#3328) Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * :shirt: Apply `node-param-option-value-duplicate` (#3331) * :shirt: Apply `node-param-description-miscased-json` (#3337) * :shirt: Apply `node-param-display-name-excess-inner-whitespace` (#3335) Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * :shirt: Apply `node-param-type-options-missing-from-limit` (#3336) * Rule workig as intended * :pencil2: Uncomment rules Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * :shirt: Apply `node-param-option-name-duplicate` (#3338) Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * :shirt: Apply `node-param-description-wrong-for-simplify` (#3334) * :zap: fix * :zap: exceptions * :zap: changed rule ignoring from file to line * :shirt: Apply `node-param-resource-without-no-data-expression` (#3339) * :shirt: Apply `node-param-display-name-untrimmed` (#3341) * :shirt: Apply `node-param-display-name-miscased-id` (#3340) Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * :shirt: Apply `node-param-resource-with-plural-option` (#3342) * :shirt: Apply `node-param-description-wrong-for-upsert` (#3333) * :zap: fix * :zap: replaced record with contact in description * :zap: fix Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * :shirt: Apply `node-param-option-description-identical-to-name` (#3343) * :shirt: Apply `node-param-option-name-containing-star` (#3347) * :shirt: Apply `node-param-display-name-wrong-for-update-fields` (#3348) * :shirt: Apply `node-param-option-name-wrong-for-get-all` (#3345) * :zap: fix * :zap: exceptions * :shirt: Apply node-param-display-name-wrong-for-simplify (#3344) * Rule working as intended * Uncomented other rules * :shirt: Undo and add exceptions Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * :zap: Alphabetize lint rules * :zap: Restore `lintfix` script Co-authored-by: Michael Kret <88898367+michael-radency@users.noreply.github.com> Co-authored-by: agobrech <45268029+agobrech@users.noreply.github.com>
2022-05-20 14:47:24 -07:00
noDataExpression: true,
options: [
{
name: 'File',
value: 'file',
},
{
name: 'Form',
value: 'form',
},
{
name: 'Hook',
value: 'hook',
},
{
name: 'Submission',
value: 'submission',
},
],
default: 'submission',
required: true,
},
...formOperations,
...formFields,
...hookOperations,
...hookFields,
...submissionOperations,
...submissionFields,
...fileOperations,
...fileFields,
],
};
methods = {
loadOptions: {
loadForms,
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
let responseData: any;
let returnData: any[] = [];
const binaryItems: INodeExecutionData[] = [];
const items = this.getInputData();
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
for (let i = 0; i < items.length; i++) {
if (resource === 'form') {
// *********************************************************************
// Form
// *********************************************************************
if (operation === 'get') {
// ----------------------------------
// Form: get
// ----------------------------------
const formId = this.getNodeParameter('formId', i) as string;
responseData = [
await koBoToolboxApiRequest.call(this, {
url: `/api/v2/assets/${formId}`,
}),
];
}
if (operation === 'getAll') {
// ----------------------------------
// Form: getAll
// ----------------------------------
const formQueryOptions = this.getNodeParameter('options', i) as {
sort: {
value: {
descending: boolean;
ordering: string;
};
};
};
const formFilterOptions = this.getNodeParameter('filters', i);
responseData = await koBoToolboxApiRequest.call(this, {
url: '/api/v2/assets/',
qs: {
limit: this.getNodeParameter('limit', i, 1000),
...(formFilterOptions.filter && { q: formFilterOptions.filter }),
...(formQueryOptions?.sort?.value?.ordering && {
ordering:
(formQueryOptions?.sort?.value?.descending ? '-' : '') +
formQueryOptions?.sort?.value?.ordering,
}),
},
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
// *********************************************************************
const formId = this.getNodeParameter('formId', i) as string;
if (operation === 'getAll') {
// ----------------------------------
// Submissions: getAll
// ----------------------------------
const submissionQueryOptions = this.getNodeParameter('options', i);
const filterJson = this.getNodeParameter('filterJson', i, null) as string;
responseData = await koBoToolboxApiRequest.call(this, {
url: `/api/v2/assets/${formId}/data/`,
qs: {
limit: this.getNodeParameter('limit', i, 1000),
...(filterJson && { query: filterJson }),
...(submissionQueryOptions.sort && { sort: submissionQueryOptions.sort }),
...(submissionQueryOptions.fields && {
fields: JSON.stringify(parseStringList(submissionQueryOptions.fields as string)),
}),
},
scroll: this.getNodeParameter('returnAll', i),
});
if (submissionQueryOptions.reformat) {
responseData = responseData.map((submission: IDataObject) => {
return formatSubmission(
submission,
parseStringList(submissionQueryOptions.selectMask as string),
parseStringList(submissionQueryOptions.numberMask as string),
);
});
}
if (submissionQueryOptions.download) {
// Download related attachments
for (const submission of responseData) {
binaryItems.push(
await downloadAttachments.call(this, submission, submissionQueryOptions),
);
}
}
}
if (operation === 'get') {
// ----------------------------------
// Submissions: get
// ----------------------------------
const submissionId = this.getNodeParameter('submissionId', i) as string;
const options = this.getNodeParameter('options', i);
responseData = [
await koBoToolboxApiRequest.call(this, {
url: `/api/v2/assets/${formId}/data/${submissionId}`,
qs: {
...(options.fields && {
fields: JSON.stringify(parseStringList(options.fields as string)),
}),
},
}),
];
if (options.reformat) {
responseData = responseData.map((submission: IDataObject) => {
return formatSubmission(
submission,
parseStringList(options.selectMask as string),
parseStringList(options.numberMask as string),
);
});
}
if (options.download) {
// Download related attachments
for (const submission of responseData) {
binaryItems.push(await downloadAttachments.call(this, submission, options));
}
}
}
if (operation === 'delete') {
// ----------------------------------
// Submissions: delete
// ----------------------------------
const id = this.getNodeParameter('submissionId', i) as string;
await koBoToolboxApiRequest.call(this, {
method: 'DELETE',
url: `/api/v2/assets/${formId}/data/${id}`,
});
responseData = [
{
success: true,
},
];
}
if (operation === 'getValidation') {
// ----------------------------------
// Submissions: getValidation
// ----------------------------------
const submissionId = this.getNodeParameter('submissionId', i) as string;
responseData = [
await koBoToolboxApiRequest.call(this, {
url: `/api/v2/assets/${formId}/data/${submissionId}/validation_status/`,
}),
];
}
if (operation === 'setValidation') {
// ----------------------------------
// Submissions: setValidation
// ----------------------------------
const submissionId = this.getNodeParameter('submissionId', i) as string;
const status = this.getNodeParameter('validationStatus', i) as string;
responseData = [
await koBoToolboxApiRequest.call(this, {
method: 'PATCH',
url: `/api/v2/assets/${formId}/data/${submissionId}/validation_status/`,
body: {
'validation_status.uid': status,
},
}),
];
}
}
if (resource === 'hook') {
const formId = this.getNodeParameter('formId', i) as string;
// *********************************************************************
// Hook
// *********************************************************************
if (operation === 'getAll') {
// ----------------------------------
// Hook: getAll
// ----------------------------------
responseData = await koBoToolboxApiRequest.call(this, {
url: `/api/v2/assets/${formId}/hooks/`,
qs: {
limit: this.getNodeParameter('limit', i, 1000),
},
scroll: this.getNodeParameter('returnAll', i),
});
}
if (operation === 'get') {
// ----------------------------------
// Hook: get
// ----------------------------------
const hookId = this.getNodeParameter('hookId', i) as string;
responseData = [
await koBoToolboxApiRequest.call(this, {
url: `/api/v2/assets/${formId}/hooks/${hookId}`,
}),
];
}
if (operation === 'retryAll') {
// ----------------------------------
// Hook: retryAll
// ----------------------------------
const hookId = this.getNodeParameter('hookId', i) as string;
responseData = [
await koBoToolboxApiRequest.call(this, {
method: 'PATCH',
url: `/api/v2/assets/${formId}/hooks/${hookId}/retry/`,
}),
];
}
if (operation === 'getLogs') {
// ----------------------------------
// Hook: getLogs
// ----------------------------------
const hookId = this.getNodeParameter('hookId', i) as string;
const startDate = this.getNodeParameter('startDate', i, null);
const endDate = this.getNodeParameter('endDate', i, null);
const status = this.getNodeParameter('status', i, null);
responseData = await koBoToolboxApiRequest.call(this, {
url: `/api/v2/assets/${formId}/hooks/${hookId}/logs/`,
qs: {
...(startDate && { start: startDate }),
...(endDate && { end: endDate }),
...(status && { status }),
},
});
}
if (operation === 'retryOne') {
// ----------------------------------
// Hook: retryOne
// ----------------------------------
const hookId = this.getNodeParameter('hookId', i) as string;
const logId = this.getNodeParameter('logId', i) as string;
responseData = [
await koBoToolboxApiRequest.call(this, {
method: 'PATCH',
url: `/api/v2/assets/${formId}/hooks/${hookId}/logs/${logId}/retry/`,
}),
];
}
}
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);
responseData = [
await koBoToolboxApiRequest.call(this, {
url: `/api/v2/assets/${formId}/files/${fileId}`,
}),
];
if (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];
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);
}
// Map data to n8n data
return binaryItems.length > 0 ? [binaryItems] : [this.helpers.returnJsonArray(returnData)];
}
}