fix(Discourse Node): Fix issue with not all posts getting returned and add credential test (#3007)

* 🔨 fix for not all posts returning

*  added credential test

*  Improvements

*  Improvements

*  Define test the new way

*  Remove not needed imports

*  Fix auth test problem

Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Ricardo Espinoza <ricardo@n8n.io>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Michael Kret 2022-04-18 20:31:59 +03:00 committed by GitHub
parent a9653c20ef
commit d68b7a4cf4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 20 deletions

View file

@ -1,5 +1,8 @@
import { import {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType, ICredentialType,
IHttpRequestOptions,
INodeProperties, INodeProperties,
} from 'n8n-workflow'; } from 'n8n-workflow';
@ -30,4 +33,25 @@ export class DiscourseApi implements ICredentialType {
default: '', default: '',
}, },
]; ];
async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
requestOptions.headers = {
'Api-Key': credentials.apiKey,
'Api-Username': credentials.username,
};
if (requestOptions.method === 'GET') {
delete requestOptions.body;
}
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.url}}',
url: '/admin/groups.json',
method: 'GET',
},
};
} }

View file

@ -38,7 +38,7 @@ export class ZendeskApi implements ICredentialType {
password: credentials.apiToken as string, password: credentials.apiToken as string,
}; };
return requestOptions; return requestOptions;
} }
test: ICredentialTestRequest = { test: ICredentialTestRequest = {
request: { request: {
baseURL: '=https://{{$credentials.subdomain}}.zendesk.com/api/v2', baseURL: '=https://{{$credentials.subdomain}}.zendesk.com/api/v2',

View file

@ -3,12 +3,16 @@ import {
} from 'n8n-core'; } from 'n8n-core';
import { import {
ICredentialsDecrypted,
ICredentialTestFunctions,
IDataObject, IDataObject,
ILoadOptionsFunctions, ILoadOptionsFunctions,
INodeCredentialTestResult,
INodeExecutionData, INodeExecutionData,
INodePropertyOptions, INodePropertyOptions,
INodeType, INodeType,
INodeTypeDescription, INodeTypeDescription,
JsonObject,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { import {
@ -44,6 +48,7 @@ import {
userGroupFields, userGroupFields,
userGroupOperations, userGroupOperations,
} from './UserGroupDescription'; } from './UserGroupDescription';
import { OptionsWithUri } from 'request';
//import moment from 'moment'; //import moment from 'moment';
@ -117,6 +122,7 @@ export class Discourse implements INodeType {
}; };
methods = { methods = {
loadOptions: { loadOptions: {
// Get all the calendars to display them to user so that he can // Get all the calendars to display them to user so that he can
// select them easily // select them easily
@ -321,6 +327,7 @@ export class Discourse implements INodeType {
//https://docs.discourse.org/#tag/Posts/paths/~1posts.json/get //https://docs.discourse.org/#tag/Posts/paths/~1posts.json/get
if (operation === 'getAll') { if (operation === 'getAll') {
const returnAll = this.getNodeParameter('returnAll', i) as boolean; const returnAll = this.getNodeParameter('returnAll', i) as boolean;
const limit = this.getNodeParameter('limit', i, 0) as number;
responseData = await discourseApiRequest.call( responseData = await discourseApiRequest.call(
this, this,
@ -329,11 +336,29 @@ export class Discourse implements INodeType {
{}, {},
qs, qs,
); );
responseData = responseData.latest_posts; responseData = responseData.latest_posts;
//Getting all posts relying on https://github.com/discourse/discourse_api/blob/main/spec/discourse_api/api/posts_spec.rb
let lastPost = responseData.pop();
let previousLastPostID;
while (lastPost.id !== previousLastPostID) {
if (limit && responseData.length > limit) {
break;
}
const chunk = await discourseApiRequest.call(
this,
'GET',
`/posts.json?before=${lastPost.id}`,
{},
qs,
);
responseData = responseData.concat(chunk.latest_posts);
previousLastPostID = lastPost.id;
lastPost = responseData.pop();
}
responseData.push(lastPost);
if (returnAll === false) { if (returnAll === false) {
const limit = this.getNodeParameter('limit', i) as number;
responseData = responseData.splice(0, limit); responseData = responseData.splice(0, limit);
} }
} }
@ -495,7 +520,7 @@ export class Discourse implements INodeType {
} }
} catch (error) { } catch (error) {
if (this.continueOnFail()) { if (this.continueOnFail()) {
returnData.push({ error: error.message }); returnData.push({ error: (error as JsonObject).message });
continue; continue;
} }
throw error; throw error;

View file

@ -9,18 +9,14 @@ import {
} from 'n8n-core'; } from 'n8n-core';
import { import {
IDataObject, NodeApiError, IDataObject, JsonObject, NodeApiError,
} from 'n8n-workflow'; } from 'n8n-workflow';
export async function discourseApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, path: string, body: any = {}, qs: IDataObject = {}, option = {}): Promise<any> { // tslint:disable-line:no-any export async function discourseApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, path: string, body: any = {}, qs: IDataObject = {}, option = {}): Promise<any> { // tslint:disable-line:no-any
const credentials = await this.getCredentials('discourseApi'); const credentials = await this.getCredentials('discourseApi') as { url: string };
const options: OptionsWithUri = { const options: OptionsWithUri = {
headers: {
'Api-Key': credentials.apiKey,
'Api-Username': credentials.username,
},
method, method,
body, body,
qs, qs,
@ -32,10 +28,9 @@ export async function discourseApiRequest(this: IExecuteFunctions | IExecuteSing
if (Object.keys(body).length === 0) { if (Object.keys(body).length === 0) {
delete options.body; delete options.body;
} }
//@ts-ignore return await this.helpers.requestWithAuthentication.call(this, 'discourseApi', options);
return await this.helpers.request.call(this, options);
} catch (error) { } catch (error) {
throw new NodeApiError(this.getNode(), error); throw new NodeApiError(this.getNode(), error as JsonObject);
} }
} }

View file

@ -1,17 +1,10 @@
import {
OptionsWithUri,
} from 'request';
import { import {
IExecuteFunctions, IExecuteFunctions,
} from 'n8n-core'; } from 'n8n-core';
import { import {
ICredentialsDecrypted,
ICredentialTestFunctions,
IDataObject, IDataObject,
ILoadOptionsFunctions, ILoadOptionsFunctions,
INodeCredentialTestResult,
INodeExecutionData, INodeExecutionData,
INodePropertyOptions, INodePropertyOptions,
INodeType, INodeType,