mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix(KoBoToolbox Node): Fix query and sort + use question name in attachments (#3017)
* Fix query,sort + use question name in attachments * Change Menu structure * kobo: Clearer webhook name * [kobo]: fix when no json filter
This commit is contained in:
parent
a7d960c561
commit
c885115768
|
@ -173,10 +173,14 @@ export async function downloadAttachments(this: IExecuteFunctions | IWebhookFunc
|
||||||
for (const [index, attachment] of attachmentList.entries()) {
|
for (const [index, attachment] of attachmentList.entries()) {
|
||||||
// look for the question name linked to this attachment
|
// look for the question name linked to this attachment
|
||||||
const filename = attachment.filename;
|
const filename = attachment.filename;
|
||||||
Object.keys(submission).forEach(question => {
|
let relatedQuestion = null;
|
||||||
if (filename.endsWith('/' + _.toString(submission[question]).replace(/\s/g, '_'))) {
|
if('question' === options.binaryNamingScheme) {
|
||||||
}
|
Object.keys(submission).forEach(question => {
|
||||||
});
|
if (filename.endsWith('/' + _.toString(submission[question]).replace(/\s/g, '_'))) {
|
||||||
|
relatedQuestion = question;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Download attachment
|
// Download attachment
|
||||||
// NOTE: this needs to follow redirects (possibly across domains), while keeping Authorization headers
|
// NOTE: this needs to follow redirects (possibly across domains), while keeping Authorization headers
|
||||||
|
@ -209,11 +213,18 @@ export async function downloadAttachments(this: IExecuteFunctions | IWebhookFunc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataPropertyAttachmentsPrefixName = options.dataPropertyAttachmentsPrefixName || 'attachment_';
|
|
||||||
const fileName = filename.split('/').pop();
|
|
||||||
|
|
||||||
if (response && response.body) {
|
if (response && response.body) {
|
||||||
binaryItem.binary![`${dataPropertyAttachmentsPrefixName}${index}`] = await this.helpers.prepareBinaryData(response.body, fileName);
|
// Use the provided prefix if any, otherwise try to use the original question name
|
||||||
|
let binaryName;
|
||||||
|
if('question' === options.binaryNamingScheme && relatedQuestion) {
|
||||||
|
binaryName = relatedQuestion;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
binaryName = `${options.dataPropertyAttachmentsPrefixName || 'attachment_'}${index}`;
|
||||||
|
}
|
||||||
|
const fileName = filename.split('/').pop();
|
||||||
|
|
||||||
|
binaryItem.binary![binaryName] = await this.helpers.prepareBinaryData(response.body, fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -194,13 +194,14 @@ export class KoBoToolbox implements INodeType {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
||||||
const submissionQueryOptions = this.getNodeParameter('options', i) as IDataObject;
|
const submissionQueryOptions = this.getNodeParameter('options', i) as IDataObject;
|
||||||
|
const filterJson = this.getNodeParameter('filterJson', i, null) as string;
|
||||||
|
|
||||||
responseData = await koBoToolboxApiRequest.call(this, {
|
responseData = await koBoToolboxApiRequest.call(this, {
|
||||||
url: `/api/v2/assets/${formId}/data/`,
|
url: `/api/v2/assets/${formId}/data/`,
|
||||||
qs: {
|
qs: {
|
||||||
limit: this.getNodeParameter('limit', i, 1000) as number,
|
limit: this.getNodeParameter('limit', i, 1000) as number,
|
||||||
...(submissionQueryOptions.query && { query: submissionQueryOptions.query }),
|
...(filterJson && { query: filterJson }),
|
||||||
//...(submissionQueryOptions.sort && { sort: submissionQueryOptions.sort }),
|
...(submissionQueryOptions.sort && { sort: submissionQueryOptions.sort }),
|
||||||
...(submissionQueryOptions.fields && { fields: JSON.stringify(parseStringList(submissionQueryOptions.fields as string)) }),
|
...(submissionQueryOptions.fields && { fields: JSON.stringify(parseStringList(submissionQueryOptions.fields as string)) }),
|
||||||
},
|
},
|
||||||
scroll: this.getNodeParameter('returnAll', i) as boolean,
|
scroll: this.getNodeParameter('returnAll', i) as boolean,
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
} from './GenericFunctions';
|
} from './GenericFunctions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
options,
|
options,
|
||||||
} from './Options';
|
} from './Options';
|
||||||
|
|
||||||
export class KoBoToolboxTrigger implements INodeType {
|
export class KoBoToolboxTrigger implements INodeType {
|
||||||
|
@ -98,13 +98,14 @@ export class KoBoToolboxTrigger implements INodeType {
|
||||||
async create(this: IHookFunctions): Promise<boolean> {
|
async create(this: IHookFunctions): Promise<boolean> {
|
||||||
const webhookData = this.getWorkflowStaticData('node');
|
const webhookData = this.getWorkflowStaticData('node');
|
||||||
const webhookUrl = this.getNodeWebhookUrl('default');
|
const webhookUrl = this.getNodeWebhookUrl('default');
|
||||||
|
const workflow = this.getWorkflow();
|
||||||
const formId = this.getNodeParameter('formId') as string; //tslint:disable-line:variable-name
|
const formId = this.getNodeParameter('formId') as string; //tslint:disable-line:variable-name
|
||||||
|
|
||||||
const response = await koBoToolboxApiRequest.call(this, {
|
const response = await koBoToolboxApiRequest.call(this, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: `/api/v2/assets/${formId}/hooks/`,
|
url: `/api/v2/assets/${formId}/hooks/`,
|
||||||
body: {
|
body: {
|
||||||
name: `n8n-webhook:${webhookUrl}`,
|
name: `n8n webhook id ${workflow.id}: ${workflow.name}`,
|
||||||
endpoint: webhookUrl,
|
endpoint: webhookUrl,
|
||||||
email_notification: true,
|
email_notification: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,6 +9,36 @@ export const options = {
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
default: {},
|
default: {},
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Download Attachments',
|
||||||
|
name: 'download',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Download submitted attachments',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Attachments Naming Scheme',
|
||||||
|
name: 'binaryNamingScheme',
|
||||||
|
type: 'options',
|
||||||
|
default: 'sequence',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
download: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Sequence (e.g. attachment_N)',
|
||||||
|
value: 'sequence',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Use Original Form Question ID',
|
||||||
|
value: 'question',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Attachments Prefix',
|
displayName: 'Attachments Prefix',
|
||||||
name: 'dataPropertyAttachmentsPrefixName',
|
name: 'dataPropertyAttachmentsPrefixName',
|
||||||
|
@ -18,18 +48,14 @@ export const options = {
|
||||||
download: [
|
download: [
|
||||||
true,
|
true,
|
||||||
],
|
],
|
||||||
|
binaryNamingScheme: [
|
||||||
|
'sequence',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: 'attachment_',
|
default: 'attachment_',
|
||||||
description: 'Prefix for name of the binary property to which to write the attachments. An index starting with 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0"',
|
description: 'Prefix for name of the binary property to which to write the attachments. An index starting with 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0"',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
displayName: 'Download Attachments',
|
|
||||||
name: 'download',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
description: 'Download submitted attachments',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
displayName: 'File Size',
|
displayName: 'File Size',
|
||||||
name: 'version',
|
name: 'version',
|
||||||
|
|
|
@ -192,6 +192,73 @@ export const submissionFields: INodeProperties[] = [
|
||||||
default: 100,
|
default: 100,
|
||||||
description: 'Max number of results to return',
|
description: 'Max number of results to return',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filter',
|
||||||
|
name: 'filterType',
|
||||||
|
type: 'options',
|
||||||
|
default: 'none',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'submission',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'None',
|
||||||
|
value: 'none',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JSON',
|
||||||
|
value: 'json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'See <a href="https://github.com/SEL-Columbia/formhub/wiki/Formhub-Access-Points-(API)#api-parameters" target="_blank">Formhub API docs</a> to creating filters, using the MongoDB JSON format - e.g. {"_submission_time":{"$lt":"2021-10-01T01:02:03"}}',
|
||||||
|
name: 'jsonNotice',
|
||||||
|
type: 'notice',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'submission',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Filters (JSON)',
|
||||||
|
name: 'filterJson',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
typeOptions: {
|
||||||
|
// alwaysOpenEditWindow: true,
|
||||||
|
},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
'submission',
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
filterType: [
|
||||||
|
'json',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Options',
|
displayName: 'Options',
|
||||||
name: 'options',
|
name: 'options',
|
||||||
|
@ -210,6 +277,36 @@ export const submissionFields: INodeProperties[] = [
|
||||||
default: {},
|
default: {},
|
||||||
placeholder: 'Add Option',
|
placeholder: 'Add Option',
|
||||||
options: [
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Download Attachments',
|
||||||
|
name: 'download',
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Download submitted attachments',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Attachments Naming Scheme',
|
||||||
|
name: 'binaryNamingScheme',
|
||||||
|
type: 'options',
|
||||||
|
default: 'sequence',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
download: [
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
name: 'Sequence (e.g. attachment_N)',
|
||||||
|
value: 'sequence',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Use Original Form Question ID',
|
||||||
|
value: 'question',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Attachments Prefix',
|
displayName: 'Attachments Prefix',
|
||||||
name: 'dataPropertyAttachmentsPrefixName',
|
name: 'dataPropertyAttachmentsPrefixName',
|
||||||
|
@ -219,18 +316,14 @@ export const submissionFields: INodeProperties[] = [
|
||||||
download: [
|
download: [
|
||||||
true,
|
true,
|
||||||
],
|
],
|
||||||
|
binaryNamingScheme: [
|
||||||
|
'sequence',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
default: 'attachment_',
|
default: 'attachment_',
|
||||||
description: 'Prefix for name of the binary property to which to write the attachments. An index starting with 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0"',
|
description: 'Prefix for name of the binary property to which to write the attachments. An index starting with 0 will be added. So if name is "attachment_" the first attachment is saved to "attachment_0"',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
displayName: 'Download Attachments',
|
|
||||||
name: 'download',
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
description: 'Download submitted attachments',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
displayName: 'Fields to Retrieve',
|
displayName: 'Fields to Retrieve',
|
||||||
name: 'fields',
|
name: 'fields',
|
||||||
|
@ -291,13 +384,13 @@ export const submissionFields: INodeProperties[] = [
|
||||||
default: false,
|
default: false,
|
||||||
description: 'Apply some reformatting to the submission data, such as parsing GeoJSON coordinates',
|
description: 'Apply some reformatting to the submission data, such as parsing GeoJSON coordinates',
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// displayName: 'Sort',
|
displayName: 'Sort',
|
||||||
// name: 'sort',
|
name: 'sort',
|
||||||
// type: 'json',
|
type: 'json',
|
||||||
// default: '',
|
default: '',
|
||||||
// description: 'Sort predicates, in Mongo JSON format (e.g. {"_submission_time":1})',
|
description: 'Sort predicates, in MongoDB JSON format (e.g. {"_submission_time":1})',
|
||||||
// },
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue