Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

n8n 3782 node improvement pr 3394 minor fix #3535

Merged
merged 14 commits into from
Jul 4, 2022
Merged
18 changes: 18 additions & 0 deletions packages/nodes-base/credentials/KoBoToolboxApi.credentials.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
Expand All @@ -23,4 +25,20 @@ export class KoBoToolboxApi implements ICredentialType {
hint: 'You can get your API token at https://[api-root]/token/?format=json (for a logged in user)',
},
];
authenticate = {
type: 'generic',
properties: {
headers: {
Authorization: '=Token {{$credentials.token}}',
},
},
} as IAuthenticateGeneric;

test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.URL}}',
url: '/api/v2/assets/',
method: 'GET',
},
};
}
40 changes: 26 additions & 14 deletions packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export async function koBoToolboxApiRequest(this: IExecuteFunctions | IWebhookFu
url: '',
headers: {
'Accept': 'application/json',
'Authorization': `Token ${credentials.token}`,
},
json: true,
};
Expand All @@ -44,12 +43,11 @@ export async function koBoToolboxApiRequest(this: IExecuteFunctions | IWebhookFu
let results = null;
let keepLooking = true;
while (keepLooking) {
const response = await this.helpers.httpRequest(options);
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'koBoToolboxApi', options);
// Append or set results
results = response.results ? _.concat(results || [], response.results) : response;
if (returnAll && response.next) {
options.url = response.next;
continue;
}
else {
keepLooking = false;
Expand All @@ -61,13 +59,14 @@ export async function koBoToolboxApiRequest(this: IExecuteFunctions | IWebhookFu

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
const coordinates = _.split(geoPoint, ' ');
if (coordinates.length >= 2 && _.every(coordinates, coord => coord && /^-?\d+(?:\.\d+)?$/.test(_.toString(coord)))) {
// NOTE: GeoJSON uses lon, lat, while most common systems use lat, lon order!
return _.concat([
return [
_.toNumber(coordinates[1]),
_.toNumber(coordinates[0]),
], _.toNumber(coordinates[2]) ? _.toNumber(coordinates[2]) : []);
];
}
return null;
}
Expand Down Expand Up @@ -102,10 +101,16 @@ const formatValue = (value: any, format: string): any => { //tslint:disable-line
const coordinates = _.compact(points.map(parseGeoPoint));
// Only return if all values are properly parsed
if (coordinates.length === points.length) {
return {
type: _.first(points) === _.last(points) ? 'Polygon' : 'LineString', // check if shape is closed or open
coordinates,
};
// If the shape is closed, declare it as Polygon, otherwise as LineString
return _.first(points) === _.last(points)
? {
type: 'Polygon',
coordinates: [coordinates],
}
: {
type: 'LineString',
coordinates,
};
}
}

Expand Down Expand Up @@ -172,14 +177,22 @@ export async function downloadAttachments(this: IExecuteFunctions | IWebhookFunc
if (attachmentList && attachmentList.length) {
for (const [index, attachment] of attachmentList.entries()) {
// look for the question name linked to this attachment
const filename = attachment.filename;
const fileName = attachment.filename;
const sanitizedFileName = _.toString(fileName).replace(/_[^_]+(?=\.\w+)/,''); // strip suffix

let relatedQuestion = null;
if('question' === options.binaryNamingScheme) {
Object.keys(submission).forEach(question => {
if (filename.endsWith('/' + _.toString(submission[question]).replace(/\s/g, '_'))) {
for(const question of Object.keys(submission)) {
// The logic to map attachments to question is sometimes ambiguous:
// - If the attachment is linked to a question, the question's value is the same as the attachment's filename (with spaces replaced by underscores)
// - BUT sometimes the attachment's filename has an extra suffix, e.g. "My_Picture_0OdlaKJ.jpg", would map to the question "picture": "My Picture.jpg"
const sanitizedQuestionValue = _.toString(submission[question]).replace(/\s/g, '_'); // replace spaces with underscores
if (sanitizedFileName === sanitizedQuestionValue) {
relatedQuestion = question;
// Just use the first match...
break;
}
});
}
}

// Download attachment
Expand Down Expand Up @@ -222,7 +235,6 @@ export async function downloadAttachments(this: IExecuteFunctions | IWebhookFunc
else {
binaryName = `${options.dataPropertyAttachmentsPrefixName || 'attachment_'}${index}`;
}
const fileName = filename.split('/').pop();

binaryItem.binary![binaryName] = await this.helpers.prepareBinaryData(response.body, fileName);
}
Expand Down
36 changes: 0 additions & 36 deletions packages/nodes-base/nodes/KoBoToolbox/KoBoToolbox.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ export class KoBoToolbox implements INodeType {
{
name: 'koBoToolboxApi',
required: true,
testedBy: 'koBoToolboxApiCredentialTest',
},
],
properties: [
Expand Down Expand Up @@ -91,41 +90,6 @@ export class KoBoToolbox implements INodeType {
};

methods = {
credentialTest: {
async koBoToolboxApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
const credentials = credential.data;
try {
const response = await this.helpers.request({
url: `${credentials!.URL}/api/v2/assets/hash`,
headers: {
'Accept': 'application/json',
'Authorization': `Token ${credentials!.token}`,
},
json: true,
});

if (response.hash) {
return {
status: 'OK',
message: 'Connection successful!',
};
}
else {
return {
status: 'Error',
message: `Credentials are not valid. Response: ${response.detail}`,
};
}
}
catch (err) {
return {
status: 'Error',
message: `Credentials validation failed: ${(err as JsonObject).message}`,
};
}
},
},

loadOptions: {
loadForms,
},
Expand Down