diff --git a/packages/nodes-base/.eslintrc.js b/packages/nodes-base/.eslintrc.js index 08b906d452..7934e2fc38 100644 --- a/packages/nodes-base/.eslintrc.js +++ b/packages/nodes-base/.eslintrc.js @@ -20,20 +20,13 @@ module.exports = { '@typescript-eslint/naming-convention': ['error', { selector: 'memberLike', format: null }], '@typescript-eslint/no-explicit-any': 'off', //812 warnings, better to fix in separate PR '@typescript-eslint/no-non-null-assertion': 'off', //665 errors, better to fix in separate PR - '@typescript-eslint/no-this-alias': 'off', - '@typescript-eslint/no-throw-literal': 'off', - '@typescript-eslint/no-unnecessary-qualifier': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unused-expressions': 'off', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/prefer-nullish-coalescing': 'off', - '@typescript-eslint/restrict-plus-operands': 'off', - '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', //1538 errors, better to fix in separate PR + '@typescript-eslint/no-unsafe-assignment': 'off', //7084 problems, better to fix in separate PR + '@typescript-eslint/no-unsafe-call': 'off', //541 errors, better to fix in separate PR + '@typescript-eslint/no-unsafe-member-access': 'off', //4591 errors, better to fix in separate PR + '@typescript-eslint/no-unsafe-return': 'off', //438 errors, better to fix in separate PR + '@typescript-eslint/no-unused-expressions': ['error', { allowTernary: true }], + '@typescript-eslint/restrict-template-expressions': 'off', //1152 errors, better to fix in separate PR '@typescript-eslint/unbound-method': 'off', '@typescript-eslint/ban-ts-comment': ['warn', { 'ts-ignore': true }], }, diff --git a/packages/nodes-base/credentials/Aws.credentials.ts b/packages/nodes-base/credentials/Aws.credentials.ts index db7ae9b505..12b8ff5ad3 100644 --- a/packages/nodes-base/credentials/Aws.credentials.ts +++ b/packages/nodes-base/credentials/Aws.credentials.ts @@ -303,7 +303,9 @@ export class Aws implements ICredentialType { } else if (service) { endpointString = `https://${service}.${credentials.region}.amazonaws.com`; } - endpoint = new URL(endpointString!.replace('{region}', credentials.region as string) + path); + endpoint = new URL( + endpointString!.replace('{region}', credentials.region as string) + (path as string), + ); } else { // If no endpoint is set, we try to decompose the path and use the default endpoint const customUrl = new URL(`${requestOptions.baseURL!}${requestOptions.url}${path ?? ''}`); diff --git a/packages/nodes-base/credentials/CalendlyApi.credentials.ts b/packages/nodes-base/credentials/CalendlyApi.credentials.ts index 49707444e8..b21757b39d 100644 --- a/packages/nodes-base/credentials/CalendlyApi.credentials.ts +++ b/packages/nodes-base/credentials/CalendlyApi.credentials.ts @@ -6,6 +6,12 @@ import { INodeProperties, } from 'n8n-workflow'; +const getAuthenticationType = (data: string): 'accessToken' | 'apiKey' => { + // The access token is a JWT, so it will always include dots to separate + // header, payoload and signature. + return data.includes('.') ? 'accessToken' : 'apiKey'; +}; + export class CalendlyApi implements ICredentialType { name = 'calendlyApi'; @@ -49,9 +55,3 @@ export class CalendlyApi implements ICredentialType { }, }; } - -const getAuthenticationType = (data: string): 'accessToken' | 'apiKey' => { - // The access token is a JWT, so it will always include dots to separate - // header, payoload and signature. - return data.includes('.') ? 'accessToken' : 'apiKey'; -}; diff --git a/packages/nodes-base/credentials/LemlistApi.credentials.ts b/packages/nodes-base/credentials/LemlistApi.credentials.ts index f8e6529ee1..15788d1604 100644 --- a/packages/nodes-base/credentials/LemlistApi.credentials.ts +++ b/packages/nodes-base/credentials/LemlistApi.credentials.ts @@ -27,7 +27,7 @@ export class LemlistApi implements ICredentialType { credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions, ): Promise { - const encodedApiKey = Buffer.from(':' + credentials.apiKey).toString('base64'); + const encodedApiKey = Buffer.from(':' + (credentials.apiKey as string)).toString('base64'); requestOptions.headers!.Authorization = `Basic ${encodedApiKey}`; requestOptions.headers!['user-agent'] = 'n8n'; return requestOptions; diff --git a/packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts b/packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts index 3aab708e60..57eb54fdc2 100644 --- a/packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ActionNetwork/GenericFunctions.ts @@ -42,6 +42,22 @@ export async function actionNetworkApiRequest( return this.helpers.requestWithAuthentication.call(this, 'actionNetworkApi', options); } +/** + * Convert an endpoint to the key needed to access data in the response. + */ +const toItemsKey = (endpoint: string) => { + // handle two-resource endpoint + if ( + endpoint.includes('/signatures') || + endpoint.includes('/attendances') || + endpoint.includes('/taggings') + ) { + endpoint = endpoint.split('/').pop()!; + } + + return `osdi:${endpoint.replace(/\//g, '')}`; +}; + export async function handleListing( this: IExecuteFunctions | ILoadOptionsFunctions, method: string, @@ -83,22 +99,6 @@ export async function handleListing( // helpers // ---------------------------------------- -/** - * Convert an endpoint to the key needed to access data in the response. - */ -const toItemsKey = (endpoint: string) => { - // handle two-resource endpoint - if ( - endpoint.includes('/signatures') || - endpoint.includes('/attendances') || - endpoint.includes('/taggings') - ) { - endpoint = endpoint.split('/').pop()!; - } - - return `osdi:${endpoint.replace(/\//g, '')}`; -}; - export const extractId = (response: LinksFieldContainer) => { return response._links.self.href.split('/').pop() ?? 'No ID'; }; @@ -257,32 +257,6 @@ export const resourceLoaders = { // ---------------------------------------- // response simplifiers // ---------------------------------------- - -export const simplifyResponse = (response: Response, resource: Resource) => { - if (resource === 'person') { - return simplifyPersonResponse(response as PersonResponse); - } else if (resource === 'petition') { - return simplifyPetitionResponse(response as PetitionResponse); - } - - const fieldsToSimplify = ['identifiers', '_links', 'action_network:sponsor', 'reminders']; - - return { - id: extractId(response), - ...omit(response, fieldsToSimplify), - }; -}; - -const simplifyPetitionResponse = (response: PetitionResponse) => { - const fieldsToSimplify = ['identifiers', '_links', 'action_network:hidden', '_embedded']; - - return { - id: extractId(response), - ...omit(response, fieldsToSimplify), - creator: simplifyPersonResponse(response._embedded['osdi:creator']), - }; -}; - const simplifyPersonResponse = (response: PersonResponse) => { const emailAddress = response.email_addresses.filter(isPrimary); const phoneNumber = response.phone_numbers.filter(isPrimary); @@ -311,3 +285,28 @@ const simplifyPersonResponse = (response: PersonResponse) => { language_spoken: response.languages_spoken[0], }; }; + +const simplifyPetitionResponse = (response: PetitionResponse) => { + const fieldsToSimplify = ['identifiers', '_links', 'action_network:hidden', '_embedded']; + + return { + id: extractId(response), + ...omit(response, fieldsToSimplify), + creator: simplifyPersonResponse(response._embedded['osdi:creator']), + }; +}; + +export const simplifyResponse = (response: Response, resource: Resource) => { + if (resource === 'person') { + return simplifyPersonResponse(response as PersonResponse); + } else if (resource === 'petition') { + return simplifyPetitionResponse(response as PetitionResponse); + } + + const fieldsToSimplify = ['identifiers', '_links', 'action_network:sponsor', 'reminders']; + + return { + id: extractId(response), + ...omit(response, fieldsToSimplify), + }; +}; diff --git a/packages/nodes-base/nodes/AcuityScheduling/GenericFunctions.ts b/packages/nodes-base/nodes/AcuityScheduling/GenericFunctions.ts index 51e3981ed7..10229a2205 100644 --- a/packages/nodes-base/nodes/AcuityScheduling/GenericFunctions.ts +++ b/packages/nodes-base/nodes/AcuityScheduling/GenericFunctions.ts @@ -32,7 +32,7 @@ export async function acuitySchedulingApiRequest( method, qs, body, - uri: uri || `https://acuityscheduling.com/api/v1${resource}`, + uri: uri ?? `https://acuityscheduling.com/api/v1${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts index 8ebeac607b..60af6bfcbd 100644 --- a/packages/nodes-base/nodes/Affinity/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Affinity/GenericFunctions.ts @@ -27,7 +27,7 @@ export async function affinityApiRequest( method, body, qs: query, - uri: uri || `${endpoint}${resource}`, + uri: uri ?? `${endpoint}${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts b/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts index 576e5b6960..978ee238e9 100644 --- a/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts +++ b/packages/nodes-base/nodes/AgileCrm/GenericFunctions.ts @@ -33,7 +33,7 @@ export async function agileCrmApiRequest( password: credentials.apiKey as string, }, qs: query, - uri: uri || `https://${credentials.subdomain}.agilecrm.com/dev/${endpoint}`, + uri: uri ?? `https://${credentials.subdomain}.agilecrm.com/dev/${endpoint}`, json: true, }; @@ -113,7 +113,7 @@ export async function agileCrmApiRequestUpdate( username: credentials.email as string, password: credentials.apiKey as string, }, - uri: uri || baseUri, + uri: uri ?? baseUri, json: true, }; diff --git a/packages/nodes-base/nodes/Airtable/GenericFunctions.ts b/packages/nodes-base/nodes/Airtable/GenericFunctions.ts index 3f24a79162..b71021970c 100644 --- a/packages/nodes-base/nodes/Airtable/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Airtable/GenericFunctions.ts @@ -34,7 +34,7 @@ export async function apiRequest( uri?: string, option: IDataObject = {}, ): Promise { - query = query || {}; + query = query ?? {}; // For some reason for some endpoints the bearer auth does not work // and it returns 404 like for the /meta request. So we always send @@ -46,7 +46,7 @@ export async function apiRequest( method, body, qs: query, - uri: uri || `https://api.airtable.com/v0/${endpoint}`, + uri: uri ?? `https://api.airtable.com/v0/${endpoint}`, useQuerystring: false, json: true, }; diff --git a/packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts b/packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts index 708d4dfddd..b4f7d16055 100644 --- a/packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts +++ b/packages/nodes-base/nodes/Amqp/AmqpTrigger.node.ts @@ -152,7 +152,6 @@ export class AmqpTrigger implements INodeType { const container = create_container(); let lastMsgId: string | number | Buffer | undefined = undefined; - const self = this; container.on('receiver_open', (context: EventContext) => { context.receiver?.add_credit(pullMessagesNumber); @@ -197,7 +196,7 @@ export class AmqpTrigger implements INodeType { data = data.body; } - self.emit([self.helpers.returnJsonArray([data as any])]); + this.emit([this.helpers.returnJsonArray([data as any])]); if (!context.receiver?.has_credit()) { setTimeout(() => { @@ -247,7 +246,7 @@ export class AmqpTrigger implements INodeType { // workflow manually. // for AMQP it doesn't make much sense to wait here but // for a new user who doesn't know how this works, it's better to wait and show a respective info message - async function manualTriggerFunction() { + const manualTriggerFunction = async () => { await new Promise((resolve, reject) => { const timeoutHandler = setTimeout(() => { reject( @@ -262,15 +261,15 @@ export class AmqpTrigger implements INodeType { // otherwise we emit all properties and their content const message = context.message as Message; if (Object.keys(message)[0] === 'body' && Object.keys(message).length === 1) { - self.emit([self.helpers.returnJsonArray([message.body])]); + this.emit([this.helpers.returnJsonArray([message.body])]); } else { - self.emit([self.helpers.returnJsonArray([message as any])]); + this.emit([this.helpers.returnJsonArray([message as any])]); } clearTimeout(timeoutHandler); resolve(true); }); }); - } + }; return { closeFunction, diff --git a/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts b/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts index bbc583d339..c49917b183 100644 --- a/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts +++ b/packages/nodes-base/nodes/ApiTemplateIo/ApiTemplateIo.node.ts @@ -441,7 +441,7 @@ export class ApiTemplateIo implements INodeType { const fileName = responseData.download_url.split('/').pop(); const binaryData = await this.helpers.prepareBinaryData( data, - options.fileName || fileName, + options.fileName ?? fileName, ); responseData = { json: responseData, @@ -525,7 +525,7 @@ export class ApiTemplateIo implements INodeType { const fileName = responseData.download_url.split('/').pop(); const binaryData = await this.helpers.prepareBinaryData( imageData, - options.fileName || fileName, + options.fileName ?? fileName, ); responseData = { json: responseData, diff --git a/packages/nodes-base/nodes/Asana/Asana.node.ts b/packages/nodes-base/nodes/Asana/Asana.node.ts index 93eab4e88b..42cb9da142 100644 --- a/packages/nodes-base/nodes/Asana/Asana.node.ts +++ b/packages/nodes-base/nodes/Asana/Asana.node.ts @@ -2000,7 +2000,7 @@ export class Asana implements INodeType { requestMethod = 'DELETE'; - endpoint = '/tasks/' + this.getNodeParameter('id', i); + endpoint = '/tasks/' + (this.getNodeParameter('id', i) as string); responseData = await asanaApiRequest.call(this, requestMethod, endpoint, body, qs); @@ -2012,7 +2012,7 @@ export class Asana implements INodeType { requestMethod = 'GET'; - endpoint = '/tasks/' + this.getNodeParameter('id', i); + endpoint = '/tasks/' + (this.getNodeParameter('id', i) as string); responseData = await asanaApiRequest.call(this, requestMethod, endpoint, body, qs); @@ -2088,7 +2088,7 @@ export class Asana implements INodeType { // ---------------------------------- requestMethod = 'PUT'; - endpoint = '/tasks/' + this.getNodeParameter('id', i); + endpoint = '/tasks/' + (this.getNodeParameter('id', i) as string); const otherProperties = this.getNodeParameter('otherProperties', i) as IDataObject; Object.assign(body, otherProperties); diff --git a/packages/nodes-base/nodes/Asana/GenericFunctions.ts b/packages/nodes-base/nodes/Asana/GenericFunctions.ts index c120e64bfb..cdb32e7faa 100644 --- a/packages/nodes-base/nodes/Asana/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Asana/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function asanaApiRequest( method, body: { data: body }, qs: query, - url: uri || `https://app.asana.com/api/1.0${endpoint}`, + url: uri ?? `https://app.asana.com/api/1.0${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/Autopilot/GenericFunctions.ts b/packages/nodes-base/nodes/Autopilot/GenericFunctions.ts index a7b322c784..03a83feb27 100644 --- a/packages/nodes-base/nodes/Autopilot/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Autopilot/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function autopilotApiRequest( method, body, qs: query, - uri: uri || `${endpoint}${resource}`, + uri: uri ?? `${endpoint}${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/nodes/Aws/AwsSns.node.ts b/packages/nodes-base/nodes/Aws/AwsSns.node.ts index 7ba9688c5f..a6551ccc2b 100644 --- a/packages/nodes-base/nodes/Aws/AwsSns.node.ts +++ b/packages/nodes-base/nodes/Aws/AwsSns.node.ts @@ -302,8 +302,8 @@ export class AwsSns implements INodeType { const params = [ 'TopicArn=' + topic, - 'Subject=' + this.getNodeParameter('subject', i), - 'Message=' + this.getNodeParameter('message', i), + 'Subject=' + (this.getNodeParameter('subject', i) as string), + 'Message=' + (this.getNodeParameter('message', i) as string), ]; const responseData = await awsApiRequestSOAP.call( diff --git a/packages/nodes-base/nodes/Aws/DynamoDB/utils.ts b/packages/nodes-base/nodes/Aws/DynamoDB/utils.ts index af9acad651..da96e87099 100644 --- a/packages/nodes-base/nodes/Aws/DynamoDB/utils.ts +++ b/packages/nodes-base/nodes/Aws/DynamoDB/utils.ts @@ -46,8 +46,7 @@ export function adjustPutItem(putItemUi: PutItemUi) { type = 'BOOL'; } else if (typeof value === 'object' && !Array.isArray(value) && value !== null) { type = 'M'; - // @ts-ignore - } else if (isNaN(value)) { + } else if (isNaN(Number(value))) { type = 'S'; } else { type = 'N'; @@ -64,6 +63,8 @@ export function simplify(item: IAttributeValue): IDataObject { for (const [attribute, value] of Object.entries(item)) { const [type, content] = Object.entries(value)[0] as [AttributeValueType, string]; + //nedded as simplify is used in decodeItem + // eslint-disable-next-line @typescript-eslint/no-use-before-define output[attribute] = decodeAttribute(type, content); } diff --git a/packages/nodes-base/nodes/Aws/ELB/AwsElb.node.ts b/packages/nodes-base/nodes/Aws/ELB/AwsElb.node.ts index d89799cab1..461743d105 100644 --- a/packages/nodes-base/nodes/Aws/ELB/AwsElb.node.ts +++ b/packages/nodes-base/nodes/Aws/ELB/AwsElb.node.ts @@ -229,10 +229,11 @@ export class AwsElb implements INodeType { const params = ['Version=2015-12-01']; params.push( - 'Certificates.member.1.CertificateArn=' + this.getNodeParameter('certificateId', i), + 'Certificates.member.1.CertificateArn=' + + (this.getNodeParameter('certificateId', i) as string), ); - params.push('ListenerArn=' + this.getNodeParameter('listenerId', i)); + params.push('ListenerArn=' + (this.getNodeParameter('listenerId', i) as string)); responseData = await awsApiRequestSOAP.call( this, @@ -265,7 +266,7 @@ export class AwsElb implements INodeType { '/?Action=DescribeListenerCertificates&' + params.join('&'), ); } else { - params.push('PageSize=' + this.getNodeParameter('limit', 0)); + params.push('PageSize=' + (this.getNodeParameter('limit', 0) as unknown as string)); responseData = await awsApiRequestSOAP.call( this, @@ -285,10 +286,11 @@ export class AwsElb implements INodeType { const params = ['Version=2015-12-01']; params.push( - 'Certificates.member.1.CertificateArn=' + this.getNodeParameter('certificateId', i), + 'Certificates.member.1.CertificateArn=' + + (this.getNodeParameter('certificateId', i) as string), ); - params.push('ListenerArn=' + this.getNodeParameter('listenerId', i)); + params.push('ListenerArn=' + (this.getNodeParameter('listenerId', i) as string)); responseData = await awsApiRequestSOAP.call( this, @@ -365,7 +367,9 @@ export class AwsElb implements INodeType { if (operation === 'delete') { const params = ['Version=2015-12-01']; - params.push('LoadBalancerArn=' + this.getNodeParameter('loadBalancerId', i)); + params.push( + 'LoadBalancerArn=' + (this.getNodeParameter('loadBalancerId', i) as string), + ); responseData = await awsApiRequestSOAP.call( this, @@ -402,7 +406,7 @@ export class AwsElb implements INodeType { '/?Action=DescribeLoadBalancers&' + params.join('&'), ); } else { - params.push('PageSize=' + this.getNodeParameter('limit', 0)); + params.push('PageSize=' + this.getNodeParameter('limit', 0).toString()); responseData = await awsApiRequestSOAP.call( this, @@ -421,7 +425,9 @@ export class AwsElb implements INodeType { if (operation === 'get') { const params = ['Version=2015-12-01']; - params.push('LoadBalancerArns.member.1=' + this.getNodeParameter('loadBalancerId', i)); + params.push( + 'LoadBalancerArns.member.1=' + (this.getNodeParameter('loadBalancerId', i) as string), + ); responseData = await awsApiRequestSOAP.call( this, diff --git a/packages/nodes-base/nodes/Aws/Transcribe/GenericFunctions.ts b/packages/nodes-base/nodes/Aws/Transcribe/GenericFunctions.ts index f383c9de14..782119747e 100644 --- a/packages/nodes-base/nodes/Aws/Transcribe/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Aws/Transcribe/GenericFunctions.ts @@ -44,7 +44,7 @@ export async function awsApiRequest( const endpoint = new URL(getEndpointForService(service, credentials) + path); // Sign AWS API request with the user credentials - const signOpts = { headers: headers || {}, host: endpoint.host, method, path, body } as Request; + const signOpts = { headers: headers ?? {}, host: endpoint.host, method, path, body } as Request; const securityHeaders = { accessKeyId: `${credentials.accessKeyId}`.trim(), secretAccessKey: `${credentials.secretAccessKey}`.trim(), diff --git a/packages/nodes-base/nodes/BambooHr/v1/actions/employee/create/execute.ts b/packages/nodes-base/nodes/BambooHr/v1/actions/employee/create/execute.ts index ee0401f05c..31579198d5 100644 --- a/packages/nodes-base/nodes/BambooHr/v1/actions/employee/create/execute.ts +++ b/packages/nodes-base/nodes/BambooHr/v1/actions/employee/create/execute.ts @@ -100,7 +100,7 @@ export async function create( ); //obtain employeeID - const rawEmployeeId = responseData.headers.location.lastIndexOf('/'); + const rawEmployeeId: number = responseData.headers.location.lastIndexOf('/'); const employeeId = responseData.headers.location.substring(rawEmployeeId + 1); //return diff --git a/packages/nodes-base/nodes/BambooHr/v1/methods/credentialTest.ts b/packages/nodes-base/nodes/BambooHr/v1/methods/credentialTest.ts index 96613b3e1b..20bb423089 100644 --- a/packages/nodes-base/nodes/BambooHr/v1/methods/credentialTest.ts +++ b/packages/nodes-base/nodes/BambooHr/v1/methods/credentialTest.ts @@ -6,25 +6,6 @@ import { INodeCredentialTestResult, } from 'n8n-workflow'; -export async function bambooHrApiCredentialTest( - this: ICredentialTestFunctions, - credential: ICredentialsDecrypted, -): Promise { - try { - await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject); - } catch (error) { - return { - status: 'Error', - message: 'The API Key included in the request is invalid', - }; - } - - return { - status: 'OK', - message: 'Connection successful!', - } as INodeCredentialTestResult; -} - async function validateCredentials( this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject, @@ -47,3 +28,22 @@ async function validateCredentials( return this.helpers.request(options); } + +export async function bambooHrApiCredentialTest( + this: ICredentialTestFunctions, + credential: ICredentialsDecrypted, +): Promise { + try { + await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject); + } catch (error) { + return { + status: 'Error', + message: 'The API Key included in the request is invalid', + }; + } + + return { + status: 'OK', + message: 'Connection successful!', + } as INodeCredentialTestResult; +} diff --git a/packages/nodes-base/nodes/BambooHr/v1/methods/loadOptions.ts b/packages/nodes-base/nodes/BambooHr/v1/methods/loadOptions.ts index 8d29d8ea8d..aa8ff99f1d 100644 --- a/packages/nodes-base/nodes/BambooHr/v1/methods/loadOptions.ts +++ b/packages/nodes-base/nodes/BambooHr/v1/methods/loadOptions.ts @@ -23,6 +23,17 @@ export async function getTimeOffTypeID( return returnData; } +//@ts-ignore +const sort = (a, b) => { + if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { + return -1; + } + if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { + return 1; + } + return 0; +}; + export async function getCompanyFileCategories( this: ILoadOptionsFunctions, ): Promise { @@ -174,14 +185,3 @@ export async function getEmployeeFields( return returnData; } - -//@ts-ignore -const sort = (a, b) => { - if (a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase()) { - return -1; - } - if (a.name.toLocaleLowerCase() > b.name.toLocaleLowerCase()) { - return 1; - } - return 0; -}; diff --git a/packages/nodes-base/nodes/Bannerbear/GenericFunctions.ts b/packages/nodes-base/nodes/Bannerbear/GenericFunctions.ts index a9f3916305..a6e40f6750 100644 --- a/packages/nodes-base/nodes/Bannerbear/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Bannerbear/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function bannerbearApiRequest( method, body, qs: query, - uri: uri || `https://api.bannerbear.com/v2${resource}`, + uri: uri ?? `https://api.bannerbear.com/v2${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts index f55259febe..23f06c90fd 100644 --- a/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Bitbucket/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function bitbucketApiRequest( }, qs, body, - uri: uri || `https://api.bitbucket.org/2.0${resource}`, + uri: uri ?? `https://api.bitbucket.org/2.0${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Bitly/GenericFunctions.ts b/packages/nodes-base/nodes/Bitly/GenericFunctions.ts index f29517a88c..75d3a2b14a 100644 --- a/packages/nodes-base/nodes/Bitly/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Bitly/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function bitlyApiRequest( method, qs, body, - uri: uri || `https://api-ssl.bitly.com/v4${resource}`, + uri: uri ?? `https://api-ssl.bitly.com/v4${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Bitwarden/GenericFunctions.ts b/packages/nodes-base/nodes/Bitwarden/GenericFunctions.ts index 29434c42aa..4c8c3e0343 100644 --- a/packages/nodes-base/nodes/Bitwarden/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Bitwarden/GenericFunctions.ts @@ -9,6 +9,26 @@ import { import { OptionsWithUri } from 'request'; +/** + * Return the access token URL based on the user's environment. + */ +async function getTokenUrl(this: IExecuteFunctions | ILoadOptionsFunctions) { + const { environment, domain } = await this.getCredentials('bitwardenApi'); + + return environment === 'cloudHosted' + ? 'https://identity.bitwarden.com/connect/token' + : `${domain}/identity/connect/token`; +} + +/** + * Return the base API URL based on the user's environment. + */ +async function getBaseUrl(this: IExecuteFunctions | ILoadOptionsFunctions) { + const { environment, domain } = await this.getCredentials('bitwardenApi'); + + return environment === 'cloudHosted' ? 'https://api.bitwarden.com' : `${domain}/api`; +} + /** * Make an authenticated API request to Bitwarden. */ @@ -106,26 +126,6 @@ export async function handleGetAll( } } -/** - * Return the access token URL based on the user's environment. - */ -async function getTokenUrl(this: IExecuteFunctions | ILoadOptionsFunctions) { - const { environment, domain } = await this.getCredentials('bitwardenApi'); - - return environment === 'cloudHosted' - ? 'https://identity.bitwarden.com/connect/token' - : `${domain}/identity/connect/token`; -} - -/** - * Return the base API URL based on the user's environment. - */ -async function getBaseUrl(this: IExecuteFunctions | ILoadOptionsFunctions) { - const { environment, domain } = await this.getCredentials('bitwardenApi'); - - return environment === 'cloudHosted' ? 'https://api.bitwarden.com' : `${domain}/api`; -} - /** * Load a resource so that it can be selected by name from a dropdown. */ @@ -138,7 +138,7 @@ export async function loadResource(this: ILoadOptionsFunctions, resource: string data.forEach(({ id, name, externalId }: { id: string; name: string; externalId?: string }) => { returnData.push({ - name: externalId || name || id, + name: externalId ?? name ?? id, value: id, }); }); diff --git a/packages/nodes-base/nodes/Box/GenericFunctions.ts b/packages/nodes-base/nodes/Box/GenericFunctions.ts index 83a333d907..bf777aae48 100644 --- a/packages/nodes-base/nodes/Box/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Box/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function boxApiRequest( method, body, qs, - uri: uri || `https://api.box.com/2.0${resource}`, + uri: uri ?? `https://api.box.com/2.0${resource}`, json: true, }; options = Object.assign({}, options, option); @@ -63,7 +63,7 @@ export async function boxApiRequestAllItems( query.offset = 0; do { responseData = await boxApiRequest.call(this, method, endpoint, body, query); - query.offset = responseData.offset + query.limit; + query.offset = (responseData.offset as number) + query.limit; returnData.push.apply(returnData, responseData[propertyName]); } while (responseData[propertyName].length !== 0); diff --git a/packages/nodes-base/nodes/Brandfetch/GenericFunctions.ts b/packages/nodes-base/nodes/Brandfetch/GenericFunctions.ts index dafeb2e9cc..dbcd28c255 100644 --- a/packages/nodes-base/nodes/Brandfetch/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Brandfetch/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function brandfetchApiRequest( method, qs, body, - uri: uri || `https://api.brandfetch.io/v1${resource}`, + uri: uri ?? `https://api.brandfetch.io/v1${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Calendly/GenericFunctions.ts b/packages/nodes-base/nodes/Calendly/GenericFunctions.ts index 7e56fdaf9b..60afd61d10 100644 --- a/packages/nodes-base/nodes/Calendly/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Calendly/GenericFunctions.ts @@ -10,6 +10,12 @@ import { IWebhookFunctions, } from 'n8n-workflow'; +export function getAuthenticationType(data: string): 'accessToken' | 'apiKey' { + // The access token is a JWT, so it will always include dots to separate + // header, payoload and signature. + return data.includes('.') ? 'accessToken' : 'apiKey'; +} + export async function calendlyApiRequest( this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, @@ -40,7 +46,7 @@ export async function calendlyApiRequest( method, body, qs: query, - uri: uri || `${endpoint}${resource}`, + uri: uri ?? `${endpoint}${resource}`, json: true, }; @@ -54,12 +60,6 @@ export async function calendlyApiRequest( return this.helpers.requestWithAuthentication.call(this, 'calendlyApi', options); } -export function getAuthenticationType(data: string): 'accessToken' | 'apiKey' { - // The access token is a JWT, so it will always include dots to separate - // header, payoload and signature. - return data.includes('.') ? 'accessToken' : 'apiKey'; -} - export async function validateCredentials( this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject, diff --git a/packages/nodes-base/nodes/CircleCi/GenericFunctions.ts b/packages/nodes-base/nodes/CircleCi/GenericFunctions.ts index 94743117ce..718800ea72 100644 --- a/packages/nodes-base/nodes/CircleCi/GenericFunctions.ts +++ b/packages/nodes-base/nodes/CircleCi/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function circleciApiRequest( method, qs, body, - uri: uri || `https://circleci.com/api/v2${resource}`, + uri: uri ?? `https://circleci.com/api/v2${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Cisco/Webex/GenericFunctions.ts b/packages/nodes-base/nodes/Cisco/Webex/GenericFunctions.ts index cc3466793c..82290bc557 100644 --- a/packages/nodes-base/nodes/Cisco/Webex/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Cisco/Webex/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function webexApiRequest( method, body, qs, - uri: uri || `https://webexapis.com/v1${resource}`, + uri: uri ?? `https://webexapis.com/v1${resource}`, json: true, }; try { @@ -128,6 +128,12 @@ export function mapResource(event: string) { )[event]; } +function removeEmptyProperties(rest: { [key: string]: any }) { + return Object.keys(rest) + .filter((k) => rest[k] !== '') + .reduce((a, k) => ({ ...a, [k]: rest[k] }), {}); +} + export function getAttachemnts(attachements: IDataObject[]) { const _attachments: IDataObject[] = []; for (const attachment of attachements) { @@ -621,12 +627,6 @@ export function getInputTextProperties(): INodeProperties[] { ]; } -function removeEmptyProperties(rest: { [key: string]: any }) { - return Object.keys(rest) - .filter((k) => rest[k] !== '') - .reduce((a, k) => ({ ...a, [k]: rest[k] }), {}); -} - export function getAutomaticSecret(credentials: ICredentialDataDecryptedObject) { const data = `${credentials.clientId},${credentials.clientSecret}`; return createHash('md5').update(data).digest('hex'); diff --git a/packages/nodes-base/nodes/Citrix/ADC/GenericFunctions.ts b/packages/nodes-base/nodes/Citrix/ADC/GenericFunctions.ts index 0e5c6c1e16..ee91d8aecd 100644 --- a/packages/nodes-base/nodes/Citrix/ADC/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Citrix/ADC/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function citrixADCApiRequest( method, body, qs, - uri: uri || `${url.replace(new RegExp('/$'), '')}/nitro/v1${resource}`, + uri: uri ?? `${url.replace(new RegExp('/$'), '')}/nitro/v1${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Clearbit/GenericFunctions.ts b/packages/nodes-base/nodes/Clearbit/GenericFunctions.ts index 8994b94966..116e4d46da 100644 --- a/packages/nodes-base/nodes/Clearbit/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Clearbit/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function clearbitApiRequest( method, qs, body, - uri: uri || `https://${api}.clearbit.com${resource}`, + uri: uri ?? `https://${api}.clearbit.com${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts index 75248a2b72..983685a20c 100644 --- a/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ClickUp/GenericFunctions.ts @@ -32,7 +32,7 @@ export async function clickupApiRequest( method, qs, body, - uri: uri || `https://api.clickup.com/api/v2${resource}`, + uri: uri ?? `https://api.clickup.com/api/v2${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Cockpit/GenericFunctions.ts b/packages/nodes-base/nodes/Cockpit/GenericFunctions.ts index 3704d2cd6c..2f8c70425a 100644 --- a/packages/nodes-base/nodes/Cockpit/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Cockpit/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function cockpitApiRequest( token: credentials.accessToken, }, body, - uri: uri || `${credentials.url}/api${resource}`, + uri: uri ?? `${credentials.url}/api${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Coda/GenericFunctions.ts b/packages/nodes-base/nodes/Coda/GenericFunctions.ts index 06355708cf..7230cf4838 100644 --- a/packages/nodes-base/nodes/Coda/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Coda/GenericFunctions.ts @@ -19,7 +19,7 @@ export async function codaApiRequest( method, qs, body, - uri: uri || `https://coda.io/apis/v1${resource}`, + uri: uri ?? `https://coda.io/apis/v1${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Code/Sandbox.ts b/packages/nodes-base/nodes/Code/Sandbox.ts index a3eb351e02..d5badd9abb 100644 --- a/packages/nodes-base/nodes/Code/Sandbox.ts +++ b/packages/nodes-base/nodes/Code/Sandbox.ts @@ -78,7 +78,7 @@ export class Sandbox extends NodeVM { // anticipate user expecting `items` to pre-exist as in Function Item node if (error.message === 'items is not defined' && !/(let|const|var) items =/.test(script)) { const quoted = error.message.replace('items', '`items`'); - error.message = quoted + '. Did you mean `$input.all()`?'; + error.message = (quoted as string) + '. Did you mean `$input.all()`?'; } throw new ExecutionError(error); @@ -189,7 +189,7 @@ export class Sandbox extends NodeVM { // anticipate user expecting `item` to pre-exist as in Function Item node if (error.message === 'item is not defined' && !/(let|const|var) item =/.test(script)) { const quoted = error.message.replace('item', '`item`'); - error.message = quoted + '. Did you mean `$input.item.json`?'; + error.message = (quoted as string) + '. Did you mean `$input.item.json`?'; } throw new ExecutionError(error, this.itemIndex); diff --git a/packages/nodes-base/nodes/Code/utils.ts b/packages/nodes-base/nodes/Code/utils.ts index 6d7a6a215e..e68f3cd27c 100644 --- a/packages/nodes-base/nodes/Code/utils.ts +++ b/packages/nodes-base/nodes/Code/utils.ts @@ -1,5 +1,13 @@ import type { IDataObject } from 'n8n-workflow'; +export function isObject(maybe: unknown): maybe is { [key: string]: unknown } { + return typeof maybe === 'object' && maybe !== null && !Array.isArray(maybe); +} + +function isTraversable(maybe: unknown): maybe is IDataObject { + return isObject(maybe) && typeof maybe.toJSON !== 'function' && Object.keys(maybe).length > 0; +} + /** * Stringify any non-standard JS objects (e.g. `Date`, `RegExp`) inside output items at any depth. */ @@ -16,14 +24,6 @@ export function standardizeOutput(output: IDataObject) { return output; } -export function isObject(maybe: unknown): maybe is { [key: string]: unknown } { - return typeof maybe === 'object' && maybe !== null && !Array.isArray(maybe); -} - -function isTraversable(maybe: unknown): maybe is IDataObject { - return isObject(maybe) && typeof maybe.toJSON !== 'function' && Object.keys(maybe).length > 0; -} - export type CodeNodeMode = 'runOnceForAllItems' | 'runOnceForEachItem'; export const REQUIRED_N8N_ITEM_KEYS = new Set(['json', 'binary', 'pairedItem']); diff --git a/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts b/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts index a53415f065..6d60c23860 100644 --- a/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts +++ b/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts @@ -442,7 +442,8 @@ export class CoinGecko implements INodeType { const currencies = this.getNodeParameter('currencies', i) as string[]; const options = this.getNodeParameter('options', i); - (qs.ids = ids), (qs.vs_currencies = currencies.join(',')); + qs.ids = ids; + qs.vs_currencies = currencies.join(','); Object.assign(qs, options); diff --git a/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts b/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts index 45ed9ee335..fe34ecd126 100644 --- a/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts +++ b/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function coinGeckoApiRequest( method, body, qs, - uri: uri || `https://api.coingecko.com/api/v3${endpoint}`, + uri: uri ?? `https://api.coingecko.com/api/v3${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts b/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts index 959cf0f790..7c3f44b169 100644 --- a/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts +++ b/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts @@ -47,14 +47,14 @@ function compareItems( differentKeys.forEach((key) => { switch (resolve) { case 'preferInput1': - different[key] = item1.json[key] || null; + different[key] = item1.json[key] ?? null; break; case 'preferInput2': - different[key] = item2.json[key] || null; + different[key] = item2.json[key] ?? null; break; default: - const input1 = item1.json[key] || null; - const input2 = item2.json[key] || null; + const input1 = item1.json[key] ?? null; + const input2 = item2.json[key] ?? null; if (skipFields.includes(key)) { skipped[key] = { input1, input2 }; } else { @@ -89,7 +89,7 @@ function combineItems( if (disableDotNotation) { entry.json[field] = match.json[field]; } else { - const value = get(match.json, field) || null; + const value = get(match.json, field) ?? null; set(entry, `json.${field}`, value); } }); @@ -159,6 +159,93 @@ function findFirstMatch( return [{ entry: data[index], index }]; } +const parseStringAndCompareToObject = (str: string, arr: IDataObject) => { + try { + const parsedArray = jsonParse(str); + return isEqual(parsedArray, arr); + } catch (error) { + return false; + } +}; + +function isFalsy(value: T) { + if (isNull(value)) return true; + if (typeof value === 'string' && value === '') return true; + if (Array.isArray(value) && value.length === 0) return true; + return false; +} + +const fuzzyCompare = + (options: IDataObject) => + (item1: T, item2: U) => { + //Fuzzy compare is disabled, so we do strict comparison + if (!options.fuzzyCompare) return isEqual(item1, item2); + + //Both types are the same, so we do strict comparison + if (!isNull(item1) && !isNull(item2) && typeof item1 === typeof item2) { + return isEqual(item1, item2); + } + + //Null, empty strings, empty arrays all treated as the same + if (isFalsy(item1) && isFalsy(item2)) return true; + + //When a field is missing in one branch and isFalsy() in another, treat them as matching + if (isFalsy(item1) && item2 === undefined) return true; + if (item1 === undefined && isFalsy(item2)) return true; + + //Compare numbers and strings representing that number + if (typeof item1 === 'number' && typeof item2 === 'string') { + return item1.toString() === item2; + } + + if (typeof item1 === 'string' && typeof item2 === 'number') { + return item1 === item2.toString(); + } + + //Compare objects/arrays and their stringified version + if (!isNull(item1) && typeof item1 === 'object' && typeof item2 === 'string') { + return parseStringAndCompareToObject(item2, item1 as IDataObject); + } + + if (!isNull(item2) && typeof item1 === 'string' && typeof item2 === 'object') { + return parseStringAndCompareToObject(item1, item2 as IDataObject); + } + + //Compare booleans and strings representing the boolean (’true’, ‘True’, ‘TRUE’) + if (typeof item1 === 'boolean' && typeof item2 === 'string') { + if (item1 === true && item2.toLocaleLowerCase() === 'true') return true; + if (item1 === false && item2.toLocaleLowerCase() === 'false') return true; + } + + if (typeof item2 === 'boolean' && typeof item1 === 'string') { + if (item2 === true && item1.toLocaleLowerCase() === 'true') return true; + if (item2 === false && item1.toLocaleLowerCase() === 'false') return true; + } + + //Compare booleans and the numbers/string 0 and 1 + if (typeof item1 === 'boolean' && typeof item2 === 'number') { + if (item1 === true && item2 === 1) return true; + if (item1 === false && item2 === 0) return true; + } + + if (typeof item2 === 'boolean' && typeof item1 === 'number') { + if (item2 === true && item1 === 1) return true; + if (item2 === false && item1 === 0) return true; + } + + if (typeof item1 === 'boolean' && typeof item2 === 'string') { + if (item1 === true && item2 === '1') return true; + if (item1 === false && item2 === '0') return true; + } + + if (typeof item2 === 'boolean' && typeof item1 === 'string') { + if (item2 === true && item1 === '1') return true; + if (item2 === false && item1 === '0') return true; + } + + return isEqual(item1, item2); + }; + export function findMatches( input1: INodeExecutionData[], input2: INodeExecutionData[], @@ -341,90 +428,3 @@ export function checkInput( } return input; } - -const fuzzyCompare = - (options: IDataObject) => - (item1: T, item2: U) => { - //Fuzzy compare is disabled, so we do strict comparison - if (!options.fuzzyCompare) return isEqual(item1, item2); - - //Both types are the same, so we do strict comparison - if (!isNull(item1) && !isNull(item2) && typeof item1 === typeof item2) { - return isEqual(item1, item2); - } - - //Null, empty strings, empty arrays all treated as the same - if (isFalsy(item1) && isFalsy(item2)) return true; - - //When a field is missing in one branch and isFalsy() in another, treat them as matching - if (isFalsy(item1) && item2 === undefined) return true; - if (item1 === undefined && isFalsy(item2)) return true; - - //Compare numbers and strings representing that number - if (typeof item1 === 'number' && typeof item2 === 'string') { - return item1.toString() === item2; - } - - if (typeof item1 === 'string' && typeof item2 === 'number') { - return item1 === item2.toString(); - } - - //Compare objects/arrays and their stringified version - if (!isNull(item1) && typeof item1 === 'object' && typeof item2 === 'string') { - return parseStringAndCompareToObject(item2, item1 as IDataObject); - } - - if (!isNull(item2) && typeof item1 === 'string' && typeof item2 === 'object') { - return parseStringAndCompareToObject(item1, item2 as IDataObject); - } - - //Compare booleans and strings representing the boolean (’true’, ‘True’, ‘TRUE’) - if (typeof item1 === 'boolean' && typeof item2 === 'string') { - if (item1 === true && item2.toLocaleLowerCase() === 'true') return true; - if (item1 === false && item2.toLocaleLowerCase() === 'false') return true; - } - - if (typeof item2 === 'boolean' && typeof item1 === 'string') { - if (item2 === true && item1.toLocaleLowerCase() === 'true') return true; - if (item2 === false && item1.toLocaleLowerCase() === 'false') return true; - } - - //Compare booleans and the numbers/string 0 and 1 - if (typeof item1 === 'boolean' && typeof item2 === 'number') { - if (item1 === true && item2 === 1) return true; - if (item1 === false && item2 === 0) return true; - } - - if (typeof item2 === 'boolean' && typeof item1 === 'number') { - if (item2 === true && item1 === 1) return true; - if (item2 === false && item1 === 0) return true; - } - - if (typeof item1 === 'boolean' && typeof item2 === 'string') { - if (item1 === true && item2 === '1') return true; - if (item1 === false && item2 === '0') return true; - } - - if (typeof item2 === 'boolean' && typeof item1 === 'string') { - if (item2 === true && item1 === '1') return true; - if (item2 === false && item1 === '0') return true; - } - - return isEqual(item1, item2); - }; - -const parseStringAndCompareToObject = (str: string, arr: IDataObject) => { - try { - const parsedArray = jsonParse(str); - return isEqual(parsedArray, arr); - } catch (error) { - return false; - } -}; - -function isFalsy(value: T) { - if (isNull(value)) return true; - if (typeof value === 'string' && value === '') return true; - if (Array.isArray(value) && value.length === 0) return true; - return false; -} diff --git a/packages/nodes-base/nodes/Contentful/GenericFunctions.ts b/packages/nodes-base/nodes/Contentful/GenericFunctions.ts index 01bd993bf2..de2d843b5b 100644 --- a/packages/nodes-base/nodes/Contentful/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Contentful/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function contentfulApiRequest( method, qs, body, - uri: uri || `https://${isPreview ? 'preview' : 'cdn'}.contentful.com${resource}`, + uri: uri ?? `https://${isPreview ? 'preview' : 'cdn'}.contentful.com${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/ConvertKit/GenericFunctions.ts b/packages/nodes-base/nodes/ConvertKit/GenericFunctions.ts index 830798b79d..6690da0702 100644 --- a/packages/nodes-base/nodes/ConvertKit/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ConvertKit/GenericFunctions.ts @@ -23,7 +23,7 @@ export async function convertKitApiRequest( method, qs, body, - uri: uri || `https://api.convertkit.com/v3${endpoint}`, + uri: uri ?? `https://api.convertkit.com/v3${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/Copper/GenericFunctions.ts b/packages/nodes-base/nodes/Copper/GenericFunctions.ts index 625d1125eb..9546099b22 100644 --- a/packages/nodes-base/nodes/Copper/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Copper/GenericFunctions.ts @@ -145,6 +145,32 @@ export const adjustLeadFields = flow(adjustCompanyFields, adjustEmail); export const adjustPersonFields = flow(adjustCompanyFields, adjustEmails); export const adjustTaskFields = flow(adjustLeadFields, adjustProjectIds); +/** + * Make an authenticated API request to Copper and return all items. + */ +export async function copperApiRequestAllItems( + this: IHookFunctions | ILoadOptionsFunctions | IExecuteFunctions, + method: string, + resource: string, + body: IDataObject = {}, + qs: IDataObject = {}, + uri = '', + option: IDataObject = {}, +) { + let responseData; + qs.page_size = 200; + let totalItems = 0; + const returnData: IDataObject[] = []; + + do { + responseData = await copperApiRequest.call(this, method, resource, body, qs, uri, option); + totalItems = responseData.headers['x-pw-total']; + returnData.push(...responseData.body); + } while (totalItems > returnData.length); + + return returnData; +} + /** * Handle a Copper listing by returning all items or up to a limit. */ @@ -176,29 +202,3 @@ export async function handleListing( ); return responseData.slice(0, limit); } - -/** - * Make an authenticated API request to Copper and return all items. - */ -export async function copperApiRequestAllItems( - this: IHookFunctions | ILoadOptionsFunctions | IExecuteFunctions, - method: string, - resource: string, - body: IDataObject = {}, - qs: IDataObject = {}, - uri = '', - option: IDataObject = {}, -) { - let responseData; - qs.page_size = 200; - let totalItems = 0; - const returnData: IDataObject[] = []; - - do { - responseData = await copperApiRequest.call(this, method, resource, body, qs, uri, option); - totalItems = responseData.headers['x-pw-total']; - returnData.push(...responseData.body); - } while (totalItems > returnData.length); - - return returnData; -} diff --git a/packages/nodes-base/nodes/Cortex/GenericFunctions.ts b/packages/nodes-base/nodes/Cortex/GenericFunctions.ts index 68829e8bd6..f99e316752 100644 --- a/packages/nodes-base/nodes/Cortex/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Cortex/GenericFunctions.ts @@ -27,7 +27,7 @@ export async function cortexApiRequest( headers: {}, method, qs: query, - uri: uri || `${credentials.host}/api${resource}`, + uri: uri ?? `${credentials.host}/api${resource}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts b/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts index 44ea83ce9c..efe92bc258 100644 --- a/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts +++ b/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts @@ -367,7 +367,9 @@ export class CrateDb implements INodeType { for (let i = 0; i < items.length; i++) { const itemCopy = getItemCopy(items[i], columns); queries.push( - pgp.helpers.update(itemCopy, cs) + pgp.as.format(where, itemCopy) + returning, + (pgp.helpers.update(itemCopy, cs) as string) + + pgp.as.format(where, itemCopy) + + returning, ); } const _updateItems = await db.multi(pgp.helpers.concat(queries)); diff --git a/packages/nodes-base/nodes/DateTime/DateTime.node.ts b/packages/nodes-base/nodes/DateTime/DateTime.node.ts index d48937f10f..e143dc0130 100644 --- a/packages/nodes-base/nodes/DateTime/DateTime.node.ts +++ b/packages/nodes-base/nodes/DateTime/DateTime.node.ts @@ -14,6 +14,37 @@ import { set } from 'lodash'; import moment from 'moment-timezone'; +function parseDateByFormat(this: IExecuteFunctions, value: string, fromFormat: string) { + const date = moment(value, fromFormat, true); + if (moment(date).isValid()) return date; + + throw new NodeOperationError( + this.getNode(), + 'Date input cannot be parsed. Please recheck the value and the "From Format" field.', + ); +} + +function getIsoValue(this: IExecuteFunctions, value: string) { + try { + return new Date(value).toISOString(); // may throw due to unpredictable input + } catch (error) { + throw new NodeOperationError( + this.getNode(), + 'Unrecognized date input. Please specify a format in the "From Format" field.', + ); + } +} + +function parseDateByDefault(this: IExecuteFunctions, value: string) { + const isoValue = getIsoValue.call(this, value); + if (moment(isoValue).isValid()) return moment(isoValue); + + throw new NodeOperationError( + this.getNode(), + 'Unrecognized date input. Please specify a format in the "From Format" field.', + ); +} + export class DateTime implements INodeType { description: INodeTypeDescription = { displayName: 'Date & Time', @@ -399,7 +430,7 @@ export class DateTime implements INodeType { newDate = moment.unix(currentDate as unknown as number); } else { if (options.fromTimezone || options.toTimezone) { - const fromTimezone = options.fromTimezone || workflowTimezone; + const fromTimezone = options.fromTimezone ?? workflowTimezone; if (options.fromFormat) { newDate = moment.tz( currentDate, @@ -517,34 +548,3 @@ export class DateTime implements INodeType { return this.prepareOutputData(returnData); } } - -function parseDateByFormat(this: IExecuteFunctions, value: string, fromFormat: string) { - const date = moment(value, fromFormat, true); - if (moment(date).isValid()) return date; - - throw new NodeOperationError( - this.getNode(), - 'Date input cannot be parsed. Please recheck the value and the "From Format" field.', - ); -} - -function parseDateByDefault(this: IExecuteFunctions, value: string) { - const isoValue = getIsoValue.call(this, value); - if (moment(isoValue).isValid()) return moment(isoValue); - - throw new NodeOperationError( - this.getNode(), - 'Unrecognized date input. Please specify a format in the "From Format" field.', - ); -} - -function getIsoValue(this: IExecuteFunctions, value: string) { - try { - return new Date(value).toISOString(); // may throw due to unpredictable input - } catch (error) { - throw new NodeOperationError( - this.getNode(), - 'Unrecognized date input. Please specify a format in the "From Format" field.', - ); - } -} diff --git a/packages/nodes-base/nodes/DeepL/GenericFunctions.ts b/packages/nodes-base/nodes/DeepL/GenericFunctions.ts index 87cc1642ca..a23fae97f3 100644 --- a/packages/nodes-base/nodes/DeepL/GenericFunctions.ts +++ b/packages/nodes-base/nodes/DeepL/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function deepLApiRequest( method, form: body, qs, - uri: uri || `${credentials.apiPlan === 'pro' ? proApiEndpoint : freeApiEndpoint}${resource}`, + uri: uri ?? `${credentials.apiPlan === 'pro' ? proApiEndpoint : freeApiEndpoint}${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Demio/GenericFunctions.ts b/packages/nodes-base/nodes/Demio/GenericFunctions.ts index b4227f8dab..7494afc7cc 100644 --- a/packages/nodes-base/nodes/Demio/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Demio/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function demioApiRequest( method, qs, body, - uri: uri || `https://my.demio.com/api/v1${resource}`, + uri: uri ?? `https://my.demio.com/api/v1${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Dhl/GenericFunctions.ts b/packages/nodes-base/nodes/Dhl/GenericFunctions.ts index c76e268d28..11e74f2b03 100644 --- a/packages/nodes-base/nodes/Dhl/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Dhl/GenericFunctions.ts @@ -33,7 +33,7 @@ export async function dhlApiRequest( method, qs, body, - uri: uri || `https://api-eu.dhl.com${path}`, + uri: uri ?? `https://api-eu.dhl.com${path}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Drift/GenericFunctions.ts b/packages/nodes-base/nodes/Drift/GenericFunctions.ts index 30e9ea8d84..66fc96bc49 100644 --- a/packages/nodes-base/nodes/Drift/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Drift/GenericFunctions.ts @@ -19,7 +19,7 @@ export async function driftApiRequest( method, body, qs: query, - uri: uri || `https://driftapi.com${resource}`, + uri: uri ?? `https://driftapi.com${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/ERPNext/GenericFunctions.ts b/packages/nodes-base/nodes/ERPNext/GenericFunctions.ts index 8cdb2d9503..80359a68c9 100644 --- a/packages/nodes-base/nodes/ERPNext/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ERPNext/GenericFunctions.ts @@ -4,6 +4,12 @@ import { IExecuteFunctions, ILoadOptionsFunctions } from 'n8n-core'; import { IDataObject, IHookFunctions, IWebhookFunctions, NodeApiError } from 'n8n-workflow'; +/** + * Return the base API URL based on the user's environment. + */ +const getBaseUrl = ({ environment, domain, subdomain }: ERPNextApiCredentials) => + environment === 'cloudHosted' ? `https://${subdomain}.erpnext.com` : domain; + export async function erpNextApiRequest( this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, @@ -24,7 +30,7 @@ export async function erpNextApiRequest( method, body, qs: query, - uri: uri || `${baseUrl}${resource}`, + uri: uri ?? `${baseUrl}${resource}`, json: true, rejectUnauthorized: !credentials.allowUnauthorizedCerts, }; @@ -77,12 +83,6 @@ export async function erpNextApiRequestAllItems( return returnData; } -/** - * Return the base API URL based on the user's environment. - */ -const getBaseUrl = ({ environment, domain, subdomain }: ERPNextApiCredentials) => - environment === 'cloudHosted' ? `https://${subdomain}.erpnext.com` : domain; - type ERPNextApiCredentials = { apiKey: string; apiSecret: string; diff --git a/packages/nodes-base/nodes/EditImage/EditImage.node.ts b/packages/nodes-base/nodes/EditImage/EditImage.node.ts index d90a0b4ef7..ab6d5b0b1f 100644 --- a/packages/nodes-base/nodes/EditImage/EditImage.node.ts +++ b/packages/nodes-base/nodes/EditImage/EditImage.node.ts @@ -1106,6 +1106,7 @@ export class EditImage implements INodeType { const operator = operationData.operator as string; const geometryString = + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands (positionX >= 0 ? '+' : '') + positionX + (positionY >= 0 ? '+' : '') + positionY; if (item.binary![operationData.dataPropertyNameComposite as string] === undefined) { @@ -1238,7 +1239,7 @@ export class EditImage implements INodeType { // Combine the lines to a single string const renderText = lines.join('\n'); - const font = options.font || operationData.font; + const font = options.font ?? operationData.font; if (font && font !== 'default') { gmInstance = gmInstance!.font(font as string); @@ -1286,6 +1287,7 @@ export class EditImage implements INodeType { const fileName = newItem.binary![dataPropertyName].fileName; if (fileName?.includes('.')) { newItem.binary![dataPropertyName].fileName = + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands fileName.split('.').slice(0, -1).join('.') + '.' + options.format; } } diff --git a/packages/nodes-base/nodes/Egoi/GenericFunctions.ts b/packages/nodes-base/nodes/Egoi/GenericFunctions.ts index 3b21e8f2f8..7c5f5079a9 100644 --- a/packages/nodes-base/nodes/Egoi/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Egoi/GenericFunctions.ts @@ -19,14 +19,6 @@ const fieldCache: { [key: string]: IDataObject[]; } = {}; -export async function getFields(this: IExecuteFunctions, listId: string) { - if (fieldCache[listId]) { - return fieldCache[listId]; - } - fieldCache[listId] = await egoiApiRequest.call(this, 'GET', `/lists/${listId}/fields`); - return fieldCache[listId]; -} - export async function egoiApiRequest( this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -61,6 +53,14 @@ export async function egoiApiRequest( } } +export async function getFields(this: IExecuteFunctions, listId: string) { + if (fieldCache[listId]) { + return fieldCache[listId]; + } + fieldCache[listId] = await egoiApiRequest.call(this, 'GET', `/lists/${listId}/fields`); + return fieldCache[listId]; +} + export async function egoiApiRequestAllItems( this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, diff --git a/packages/nodes-base/nodes/Elastic/ElasticSecurity/ElasticSecurity.node.ts b/packages/nodes-base/nodes/Elastic/ElasticSecurity/ElasticSecurity.node.ts index d78c9e2fb0..f044b78537 100644 --- a/packages/nodes-base/nodes/Elastic/ElasticSecurity/ElasticSecurity.node.ts +++ b/packages/nodes-base/nodes/Elastic/ElasticSecurity/ElasticSecurity.node.ts @@ -447,7 +447,7 @@ export class ElasticSecurity implements INodeType { const body = { comment: this.getNodeParameter('comment', i), type: 'user', - owner: additionalFields.owner || 'securitySolution', + owner: additionalFields.owner ?? 'securitySolution', } as IDataObject; const caseId = this.getNodeParameter('caseId', i); diff --git a/packages/nodes-base/nodes/Elastic/ElasticSecurity/GenericFunctions.ts b/packages/nodes-base/nodes/Elastic/ElasticSecurity/GenericFunctions.ts index b09087f97e..9e48d730cc 100644 --- a/packages/nodes-base/nodes/Elastic/ElasticSecurity/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Elastic/ElasticSecurity/GenericFunctions.ts @@ -6,6 +6,10 @@ import { OptionsWithUri } from 'request'; import { Connector, ElasticSecurityApiCredentials } from './types'; +export function tolerateTrailingSlash(baseUrl: string) { + return baseUrl.endsWith('/') ? baseUrl.substr(0, baseUrl.length - 1) : baseUrl; +} + export async function elasticSecurityApiRequest( this: IExecuteFunctions | ILoadOptionsFunctions, method: string, @@ -138,7 +142,3 @@ export async function getVersion(this: IExecuteFunctions, endpoint: string) { return version; } - -export function tolerateTrailingSlash(baseUrl: string) { - return baseUrl.endsWith('/') ? baseUrl.substr(0, baseUrl.length - 1) : baseUrl; -} diff --git a/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts b/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts index 8fb99c4eb7..8fc8fe019a 100644 --- a/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts +++ b/packages/nodes-base/nodes/EmailReadImap/v1/EmailReadImapV1.node.ts @@ -30,6 +30,42 @@ import { simpleParser, Source as ParserSource } from 'mailparser'; import _ from 'lodash'; +export async function parseRawEmail( + this: ITriggerFunctions, + messageEncoded: ParserSource, + dataPropertyNameDownload: string, +): Promise { + const responseData = await simpleParser(messageEncoded); + const headers: IDataObject = {}; + for (const header of responseData.headerLines) { + headers[header.key] = header.line; + } + + // @ts-ignore + responseData.headers = headers; + // @ts-ignore + responseData.headerLines = undefined; + + const binaryData: IBinaryKeyData = {}; + if (responseData.attachments) { + for (let i = 0; i < responseData.attachments.length; i++) { + const attachment = responseData.attachments[i]; + binaryData[`${dataPropertyNameDownload}${i}`] = await this.helpers.prepareBinaryData( + attachment.content, + attachment.filename, + attachment.contentType, + ); + } + // @ts-ignore + responseData.attachments = undefined; + } + + return { + json: responseData as unknown as IDataObject, + binary: Object.keys(binaryData).length ? binaryData : undefined, + } as INodeExecutionData; +} + const versionDescription: INodeTypeDescription = { displayName: 'Email Trigger (IMAP)', name: 'emailReadImap', @@ -272,7 +308,6 @@ export class EmailReadImapV1 implements INodeType { // Returns the email attachments const getAttachment = async ( imapConnection: ImapSimple, - // tslint:disable-next-line:no-any parts: any[], message: Message, ): Promise => { @@ -599,39 +634,3 @@ export class EmailReadImapV1 implements INodeType { }; } } - -export async function parseRawEmail( - this: ITriggerFunctions, - messageEncoded: ParserSource, - dataPropertyNameDownload: string, -): Promise { - const responseData = await simpleParser(messageEncoded); - const headers: IDataObject = {}; - for (const header of responseData.headerLines) { - headers[header.key] = header.line; - } - - // @ts-ignore - responseData.headers = headers; - // @ts-ignore - responseData.headerLines = undefined; - - const binaryData: IBinaryKeyData = {}; - if (responseData.attachments) { - for (let i = 0; i < responseData.attachments.length; i++) { - const attachment = responseData.attachments[i]; - binaryData[`${dataPropertyNameDownload}${i}`] = await this.helpers.prepareBinaryData( - attachment.content, - attachment.filename, - attachment.contentType, - ); - } - // @ts-ignore - responseData.attachments = undefined; - } - - return { - json: responseData as unknown as IDataObject, - binary: Object.keys(binaryData).length ? binaryData : undefined, - } as INodeExecutionData; -} diff --git a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts index 3222321108..3fc7eafe12 100644 --- a/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts +++ b/packages/nodes-base/nodes/EmailReadImap/v2/EmailReadImapV2.node.ts @@ -30,6 +30,42 @@ import { simpleParser, Source as ParserSource } from 'mailparser'; import _ from 'lodash'; import { ICredentialsDataImap, isCredentialsDataImap } from '../../../credentials/Imap.credentials'; +export async function parseRawEmail( + this: ITriggerFunctions, + messageEncoded: ParserSource, + dataPropertyNameDownload: string, +): Promise { + const responseData = await simpleParser(messageEncoded); + const headers: IDataObject = {}; + for (const header of responseData.headerLines) { + headers[header.key] = header.line; + } + + // @ts-ignore + responseData.headers = headers; + // @ts-ignore + responseData.headerLines = undefined; + + const binaryData: IBinaryKeyData = {}; + if (responseData.attachments) { + for (let i = 0; i < responseData.attachments.length; i++) { + const attachment = responseData.attachments[i]; + binaryData[`${dataPropertyNameDownload}${i}`] = await this.helpers.prepareBinaryData( + attachment.content, + attachment.filename, + attachment.contentType, + ); + } + // @ts-ignore + responseData.attachments = undefined; + } + + return { + json: responseData as unknown as IDataObject, + binary: Object.keys(binaryData).length ? binaryData : undefined, + } as INodeExecutionData; +} + const versionDescription: INodeTypeDescription = { displayName: 'Email Trigger (IMAP)', name: 'emailReadImap', @@ -279,8 +315,6 @@ export class EmailReadImapV2 implements INodeType { // Returns the email attachments const getAttachment = async ( imapConnection: ImapSimple, - // tslint:disable-next-line:no-any - parts: any[], message: Message, ): Promise => { @@ -571,6 +605,7 @@ export class EmailReadImapV2 implements INodeType { conn.on('error', async (error) => { const errorCode = error.code.toUpperCase(); Logger.verbose(`IMAP connection experienced an error: (${errorCode})`, { error }); + // eslint-disable-next-line @typescript-eslint/no-use-before-define await closeFunction(); this.emitError(error); }); @@ -619,39 +654,3 @@ export class EmailReadImapV2 implements INodeType { }; } } - -export async function parseRawEmail( - this: ITriggerFunctions, - messageEncoded: ParserSource, - dataPropertyNameDownload: string, -): Promise { - const responseData = await simpleParser(messageEncoded); - const headers: IDataObject = {}; - for (const header of responseData.headerLines) { - headers[header.key] = header.line; - } - - // @ts-ignore - responseData.headers = headers; - // @ts-ignore - responseData.headerLines = undefined; - - const binaryData: IBinaryKeyData = {}; - if (responseData.attachments) { - for (let i = 0; i < responseData.attachments.length; i++) { - const attachment = responseData.attachments[i]; - binaryData[`${dataPropertyNameDownload}${i}`] = await this.helpers.prepareBinaryData( - attachment.content, - attachment.filename, - attachment.contentType, - ); - } - // @ts-ignore - responseData.attachments = undefined; - } - - return { - json: responseData as unknown as IDataObject, - binary: Object.keys(binaryData).length ? binaryData : undefined, - } as INodeExecutionData; -} diff --git a/packages/nodes-base/nodes/EmailSend/EmailSend.node.ts b/packages/nodes-base/nodes/EmailSend/EmailSend.node.ts index f6bbaf5eca..a6fdcc8d54 100644 --- a/packages/nodes-base/nodes/EmailSend/EmailSend.node.ts +++ b/packages/nodes-base/nodes/EmailSend/EmailSend.node.ts @@ -193,7 +193,7 @@ export class EmailSend implements INodeType { continue; } attachments.push({ - filename: item.binary[propertyName].fileName || 'unknown', + filename: item.binary[propertyName].fileName ?? 'unknown', content: await this.helpers.getBinaryDataBuffer(itemIndex, propertyName), }); } diff --git a/packages/nodes-base/nodes/Emelia/GenericFunctions.ts b/packages/nodes-base/nodes/Emelia/GenericFunctions.ts index c1998410fc..deb6a7845f 100644 --- a/packages/nodes-base/nodes/Emelia/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Emelia/GenericFunctions.ts @@ -10,22 +10,6 @@ import { NodeApiError, } from 'n8n-workflow'; -/** - * Make an authenticated GraphQL request to Emelia. - */ -export async function emeliaGraphqlRequest( - this: IExecuteFunctions | ILoadOptionsFunctions, - body: object = {}, -) { - const response = await emeliaApiRequest.call(this, 'POST', '/graphql', body); - - if (response.errors) { - throw new NodeApiError(this.getNode(), response); - } - - return response; -} - /** * Make an authenticated REST API request to Emelia, used for trigger node. */ @@ -56,6 +40,22 @@ export async function emeliaApiRequest( } } +/** + * Make an authenticated GraphQL request to Emelia. + */ +export async function emeliaGraphqlRequest( + this: IExecuteFunctions | ILoadOptionsFunctions, + body: object = {}, +) { + const response = await emeliaApiRequest.call(this, 'POST', '/graphql', body); + + if (response.errors) { + throw new NodeApiError(this.getNode(), response); + } + + return response; +} + /** * Load resources so that the user can select them easily. */ diff --git a/packages/nodes-base/nodes/Eventbrite/GenericFunctions.ts b/packages/nodes-base/nodes/Eventbrite/GenericFunctions.ts index ddaec221b0..827f0eef67 100644 --- a/packages/nodes-base/nodes/Eventbrite/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Eventbrite/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function eventbriteApiRequest( method, qs, body, - uri: uri || `https://www.eventbriteapi.com/v3${resource}`, + uri: uri ?? `https://www.eventbriteapi.com/v3${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/ExecuteCommand/ExecuteCommand.node.ts b/packages/nodes-base/nodes/ExecuteCommand/ExecuteCommand.node.ts index eef2d8b94c..95c1dd9ed5 100644 --- a/packages/nodes-base/nodes/ExecuteCommand/ExecuteCommand.node.ts +++ b/packages/nodes-base/nodes/ExecuteCommand/ExecuteCommand.node.ts @@ -38,7 +38,7 @@ async function execPromise(command: string): Promise { resolve(returnData); }).on('exit', (code) => { - returnData.exitCode = code || 0; + returnData.exitCode = code ?? 0; }); }); } diff --git a/packages/nodes-base/nodes/Facebook/GenericFunctions.ts b/packages/nodes-base/nodes/Facebook/GenericFunctions.ts index f6b47a4e3a..c09cee0145 100644 --- a/packages/nodes-base/nodes/Facebook/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Facebook/GenericFunctions.ts @@ -45,7 +45,7 @@ export async function facebookApiRequest( qs, body, gzip: true, - uri: uri || `https://graph.facebook.com/v8.0${resource}`, + uri: uri ?? `https://graph.facebook.com/v8.0${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Figma/GenericFunctions.ts b/packages/nodes-base/nodes/Figma/GenericFunctions.ts index 7aa17fd22f..b6556a464d 100644 --- a/packages/nodes-base/nodes/Figma/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Figma/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function figmaApiRequest( headers: { 'X-FIGMA-TOKEN': credentials.accessToken }, method, body, - uri: uri || `https://api.figma.com${resource}`, + uri: uri ?? `https://api.figma.com${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/FileMaker/GenericFunctions.ts b/packages/nodes-base/nodes/FileMaker/GenericFunctions.ts index 43abc3282e..d0a79f5012 100644 --- a/packages/nodes-base/nodes/FileMaker/GenericFunctions.ts +++ b/packages/nodes-base/nodes/FileMaker/GenericFunctions.ts @@ -5,12 +5,12 @@ import { IDataObject, INodePropertyOptions, NodeApiError, NodeOperationError } f import { OptionsWithUri } from 'request'; interface ScriptsOptions { - script?: any; //tslint:disable-line:no-any - 'script.param'?: any; //tslint:disable-line:no-any - 'script.prerequest'?: any; //tslint:disable-line:no-any - 'script.prerequest.param'?: any; //tslint:disable-line:no-any - 'script.presort'?: any; //tslint:disable-line:no-any - 'script.presort.param'?: any; //tslint:disable-line:no-any + script?: any; + 'script.param'?: any; + 'script.prerequest'?: any; + 'script.prerequest.param'?: any; + 'script.presort'?: any; + 'script.presort.param'?: any; } interface LayoutObject { name: string; @@ -24,6 +24,71 @@ interface ScriptObject { folderScriptNames?: LayoutObject[]; } +export async function getToken( + this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions, +): Promise { + const credentials = await this.getCredentials('fileMaker'); + + const host = credentials.host as string; + const db = credentials.db as string; + const login = credentials.login as string; + const password = credentials.password as string; + + const url = `https://${host}/fmi/data/v1/databases/${db}/sessions`; + + // Reset all values + const requestOptions: OptionsWithUri = { + uri: url, + headers: {}, + method: 'POST', + json: true, + //rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean, + }; + requestOptions.auth = { + user: login, + pass: password, + }; + requestOptions.body = { + fmDataSource: [ + { + database: host, + username: login, + password, + }, + ], + }; + + try { + const response = await this.helpers.request(requestOptions); + + if (typeof response === 'string') { + throw new NodeOperationError( + this.getNode(), + 'Response body is not valid JSON. Change "Response Format" to "String"', + ); + } + + return response.response.token; + } catch (error) { + throw new NodeApiError(this.getNode(), error); + } +} + +function parseLayouts(layouts: LayoutObject[]): INodePropertyOptions[] { + const returnData: INodePropertyOptions[] = []; + for (const layout of layouts) { + if (layout.isFolder!) { + returnData.push(...parseLayouts(layout.folderLayoutNames!)); + } else { + returnData.push({ + name: layout.name, + value: layout.name, + }); + } + } + return returnData; +} + /** * Make an API request to ActiveCampaign * @@ -57,21 +122,6 @@ export async function layoutsApiRequest( } } -function parseLayouts(layouts: LayoutObject[]): INodePropertyOptions[] { - const returnData: INodePropertyOptions[] = []; - for (const layout of layouts) { - if (layout.isFolder!) { - returnData.push(...parseLayouts(layout.folderLayoutNames!)); - } else { - returnData.push({ - name: layout.name, - value: layout.name, - }); - } - } - return returnData; -} - /** * Make an API request to ActiveCampaign * @@ -134,6 +184,21 @@ export async function getPortals(this: ILoadOptionsFunctions): Promise { } } +function parseScriptsList(scripts: ScriptObject[]): INodePropertyOptions[] { + const returnData: INodePropertyOptions[] = []; + for (const script of scripts) { + if (script.isFolder!) { + returnData.push(...parseScriptsList(script.folderScriptNames!)); + } else if (script.name !== '-') { + returnData.push({ + name: script.name, + value: script.name, + }); + } + } + return returnData; +} + /** * Make an API request to ActiveCampaign * @@ -166,71 +231,6 @@ export async function getScripts(this: ILoadOptionsFunctions): Promise { } } -function parseScriptsList(scripts: ScriptObject[]): INodePropertyOptions[] { - const returnData: INodePropertyOptions[] = []; - for (const script of scripts) { - if (script.isFolder!) { - returnData.push(...parseScriptsList(script.folderScriptNames!)); - } else if (script.name !== '-') { - returnData.push({ - name: script.name, - value: script.name, - }); - } - } - return returnData; -} - -export async function getToken( - this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions, -): Promise { - const credentials = await this.getCredentials('fileMaker'); - - const host = credentials.host as string; - const db = credentials.db as string; - const login = credentials.login as string; - const password = credentials.password as string; - - const url = `https://${host}/fmi/data/v1/databases/${db}/sessions`; - - // Reset all values - const requestOptions: OptionsWithUri = { - uri: url, - headers: {}, - method: 'POST', - json: true, - //rejectUnauthorized: !this.getNodeParameter('allowUnauthorizedCerts', itemIndex, false) as boolean, - }; - requestOptions.auth = { - user: login, - pass: password, - }; - requestOptions.body = { - fmDataSource: [ - { - database: host, - username: login, - password, - }, - ], - }; - - try { - const response = await this.helpers.request(requestOptions); - - if (typeof response === 'string') { - throw new NodeOperationError( - this.getNode(), - 'Response body is not valid JSON. Change "Response Format" to "String"', - ); - } - - return response.response.token; - } catch (error) { - throw new NodeApiError(this.getNode(), error); - } -} - export async function logout( this: ILoadOptionsFunctions | IExecuteFunctions | IExecuteSingleFunctions, token: string, @@ -263,11 +263,10 @@ export async function logout( return response; } catch (error) { - const errorMessage = - error.response.body.messages[0].message + '(' + error.response.body.messages[0].message + ')'; + const errorMessage = `${error.response.body.messages[0].message}'(' + ${error.response.body.messages[0].message}')'`; if (errorMessage !== undefined) { - throw errorMessage; + throw new Error(errorMessage); } throw error.response.body; } @@ -282,9 +281,7 @@ export function parseSort(this: IExecuteFunctions, i: number): object | null { sort = []; const sortParametersUi = this.getNodeParameter('sortParametersUi', i, {}) as IDataObject; if (sortParametersUi.rules !== undefined) { - // @ts-ignore for (const parameterData of sortParametersUi.rules as IDataObject[]) { - // @ts-ignore sort.push({ fieldName: parameterData.name as string, sortOrder: parameterData.value, @@ -328,47 +325,40 @@ export function parsePortals(this: IExecuteFunctions, i: number): object | null } else { portals = this.getNodeParameter('portals', i); } - // @ts-ignore - return portals; + return portals as IDataObject; } export function parseQuery(this: IExecuteFunctions, i: number): object | null { let queries; const queriesParamUi = this.getNodeParameter('queries', i, {}) as IDataObject; if (queriesParamUi.query !== undefined) { - // @ts-ignore queries = []; for (const queryParam of queriesParamUi.query as IDataObject[]) { - const query = { + const query: IDataObject = { omit: queryParam.omit ? 'true' : 'false', }; - // @ts-ignore - for (const field of queryParam.fields!.field as IDataObject[]) { - // @ts-ignore - query[field.name] = field.value; + for (const field of (queryParam.fields as IDataObject).field as IDataObject[]) { + query[field.name as string] = field.value; } queries.push(query); } } else { queries = null; } - // @ts-ignore return queries; } export function parseFields(this: IExecuteFunctions, i: number): object | null { - let fieldData; + let fieldData: IDataObject | null; const fieldsParametersUi = this.getNodeParameter('fieldsParametersUi', i, {}) as IDataObject; if (fieldsParametersUi.fields !== undefined) { - // @ts-ignore fieldData = {}; for (const field of fieldsParametersUi.fields as IDataObject[]) { - // @ts-ignore - fieldData[field.name] = field.value; + fieldData[field.name as string] = field.value; } } else { fieldData = null; } - // @ts-ignore + return fieldData; } diff --git a/packages/nodes-base/nodes/Flow/GenericFunctions.ts b/packages/nodes-base/nodes/Flow/GenericFunctions.ts index a784cb926b..34f18a87a4 100644 --- a/packages/nodes-base/nodes/Flow/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Flow/GenericFunctions.ts @@ -24,7 +24,7 @@ export async function flowApiRequest( method, qs, body, - uri: uri || `https://api.getflow.com/v2${resource}`, + uri: uri ?? `https://api.getflow.com/v2${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/FormIo/GenericFunctions.ts b/packages/nodes-base/nodes/FormIo/GenericFunctions.ts index cf4aefac96..14ff66d04a 100644 --- a/packages/nodes-base/nodes/FormIo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/FormIo/GenericFunctions.ts @@ -16,7 +16,7 @@ async function getToken( this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, credentials: IFormIoCredentials, ) { - const base = credentials.domain || 'https://formio.form.io'; + const base = credentials.domain ?? 'https://formio.form.io'; const options = { headers: { 'Content-Type': 'application/json', @@ -57,7 +57,7 @@ export async function formIoApiRequest( const token = await getToken.call(this, credentials); - const base = credentials.domain || 'https://api.form.io'; + const base = credentials.domain ?? 'https://api.form.io'; const options = { headers: { diff --git a/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts b/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts index 02c2c79e0c..69b3975485 100644 --- a/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Freshdesk/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function freshdeskApiRequest( method, body, qs: query, - uri: uri || `https://${credentials.domain}.${endpoint}${resource}`, + uri: uri ?? `https://${credentials.domain}.${endpoint}${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/nodes/Ftp/Ftp.node.ts b/packages/nodes-base/nodes/Ftp/Ftp.node.ts index 543067afb8..383de57886 100644 --- a/packages/nodes-base/nodes/Ftp/Ftp.node.ts +++ b/packages/nodes-base/nodes/Ftp/Ftp.node.ts @@ -34,6 +34,72 @@ interface ReturnFtpItem { path: string; } +async function callRecursiveList( + path: string, + client: sftpClient | ftpClient, + normalizeFunction: ( + input: ftpClient.ListingElement & sftpClient.FileInfo, + path: string, + recursive?: boolean, + ) => void, +) { + const pathArray: string[] = [path]; + let currentPath = path; + const directoryItems: sftpClient.FileInfo[] = []; + let index = 0; + + const prepareAndNormalize = (item: sftpClient.FileInfo) => { + if (pathArray[index].endsWith('/')) { + currentPath = `${pathArray[index]}${item.name}`; + } else { + currentPath = `${pathArray[index]}/${item.name}`; + } + + // Is directory + if (item.type === 'd') { + pathArray.push(currentPath); + } + + normalizeFunction(item as ftpClient.ListingElement & sftpClient.FileInfo, currentPath, true); + directoryItems.push(item); + }; + + do { + const returnData: sftpClient.FileInfo[] | Array = + await client.list(pathArray[index]); + + // @ts-ignore + returnData.map(prepareAndNormalize); + index++; + } while (index <= pathArray.length - 1); + + return directoryItems; +} + +async function recursivelyCreateSftpDirs(sftp: sftpClient, path: string) { + const dirPath = dirname(path); + const dirExists = await sftp.exists(dirPath); + + if (!dirExists) { + await sftp.mkdir(dirPath, true); + } +} + +function normalizeSFtpItem(input: sftpClient.FileInfo, path: string, recursive = false) { + const item = input as unknown as ReturnFtpItem; + item.accessTime = new Date(input.accessTime); + item.modifyTime = new Date(input.modifyTime); + item.path = !recursive ? `${path}${path.endsWith('/') ? '' : '/'}${item.name}` : path; +} + +function normalizeFtpItem(input: ftpClient.ListingElement, path: string, recursive = false) { + const item = input as unknown as ReturnFtpItem; + item.modifyTime = input.date; + item.path = !recursive ? `${path}${path.endsWith('/') ? '' : '/'}${item.name}` : path; + //@ts-ignore + item.date = undefined; +} + export class Ftp implements INodeType { description: INodeTypeDescription = { displayName: 'FTP', @@ -710,70 +776,3 @@ export class Ftp implements INodeType { return [returnItems]; } } - -function normalizeFtpItem(input: ftpClient.ListingElement, path: string, recursive = false) { - const item = input as unknown as ReturnFtpItem; - item.modifyTime = input.date; - item.path = !recursive ? `${path}${path.endsWith('/') ? '' : '/'}${item.name}` : path; - //@ts-ignore - item.date = undefined; -} - -function normalizeSFtpItem(input: sftpClient.FileInfo, path: string, recursive = false) { - const item = input as unknown as ReturnFtpItem; - item.accessTime = new Date(input.accessTime); - item.modifyTime = new Date(input.modifyTime); - item.path = !recursive ? `${path}${path.endsWith('/') ? '' : '/'}${item.name}` : path; -} - -async function callRecursiveList( - path: string, - client: sftpClient | ftpClient, - normalizeFunction: ( - input: ftpClient.ListingElement & sftpClient.FileInfo, - path: string, - recursive?: boolean, - ) => void, -) { - const pathArray: string[] = [path]; - let currentPath = path; - const directoryItems: sftpClient.FileInfo[] = []; - let index = 0; - - const prepareAndNormalize = (item: sftpClient.FileInfo) => { - if (pathArray[index].endsWith('/')) { - currentPath = `${pathArray[index]}${item.name}`; - } else { - currentPath = `${pathArray[index]}/${item.name}`; - } - - // Is directory - if (item.type === 'd') { - pathArray.push(currentPath); - } - - normalizeFunction(item as ftpClient.ListingElement & sftpClient.FileInfo, currentPath, true); - directoryItems.push(item); - }; - - do { - // tslint:disable-next-line: array-type - const returnData: sftpClient.FileInfo[] | Array = - await client.list(pathArray[index]); - - // @ts-ignore - returnData.map(prepareAndNormalize); - index++; - } while (index <= pathArray.length - 1); - - return directoryItems; -} - -async function recursivelyCreateSftpDirs(sftp: sftpClient, path: string) { - const dirPath = dirname(path); - const dirExists = await sftp.exists(dirPath); - - if (!dirExists) { - await sftp.mkdir(dirPath, true); - } -} diff --git a/packages/nodes-base/nodes/Function/Function.node.ts b/packages/nodes-base/nodes/Function/Function.node.ts index 97f81c1d76..e2ce126f1e 100644 --- a/packages/nodes-base/nodes/Function/Function.node.ts +++ b/packages/nodes-base/nodes/Function/Function.node.ts @@ -1,3 +1,4 @@ +import { NodeVM, NodeVMOptions } from 'vm2'; import { IExecuteFunctions } from 'n8n-core'; import { deepCopy, @@ -9,8 +10,6 @@ import { NodeOperationError, } from 'n8n-workflow'; -const { NodeVM } = require('vm2'); - export class Function implements INodeType { description: INodeTypeDescription = { displayName: 'Function', @@ -148,21 +147,24 @@ return items;`, const mode = this.getMode(); - const options = { + const options: NodeVMOptions = { console: mode === 'manual' ? 'redirect' : 'inherit', sandbox, require: { - external: false as boolean | { modules: string[] }, + external: false as boolean | { modules: string[]; transitive: boolean }, builtin: [] as string[], }, }; - if (process.env.NODE_FUNCTION_ALLOW_BUILTIN) { + if (process.env.NODE_FUNCTION_ALLOW_BUILTIN && typeof options.require === 'object') { options.require.builtin = process.env.NODE_FUNCTION_ALLOW_BUILTIN.split(','); } - if (process.env.NODE_FUNCTION_ALLOW_EXTERNAL) { - options.require.external = { modules: process.env.NODE_FUNCTION_ALLOW_EXTERNAL.split(',') }; + if (process.env.NODE_FUNCTION_ALLOW_EXTERNAL && typeof options.require === 'object') { + options.require.external = { + modules: process.env.NODE_FUNCTION_ALLOW_EXTERNAL.split(','), + transitive: false, + }; } const vm = new NodeVM(options); diff --git a/packages/nodes-base/nodes/FunctionItem/FunctionItem.node.ts b/packages/nodes-base/nodes/FunctionItem/FunctionItem.node.ts index 7221634b0e..57b2ab978c 100644 --- a/packages/nodes-base/nodes/FunctionItem/FunctionItem.node.ts +++ b/packages/nodes-base/nodes/FunctionItem/FunctionItem.node.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-loop-func */ +import { NodeVM, NodeVMOptions, VMRequire } from 'vm2'; import { IExecuteFunctions } from 'n8n-core'; import { deepCopy, @@ -10,8 +11,6 @@ import { NodeOperationError, } from 'n8n-workflow'; -const { NodeVM } = require('vm2'); - export class FunctionItem implements INodeType { description: INodeTypeDescription = { displayName: 'Function Item', @@ -156,26 +155,28 @@ return item;`, const dataProxy = this.getWorkflowDataProxy(itemIndex); Object.assign(sandbox, dataProxy); - const options = { + const options: NodeVMOptions = { console: mode === 'manual' ? 'redirect' : 'inherit', sandbox, require: { - external: false as boolean | { modules: string[] }, - builtin: [] as string[], + external: false, + builtin: [], }, }; + const vmRequire = options.require as VMRequire; if (process.env.NODE_FUNCTION_ALLOW_BUILTIN) { - options.require.builtin = process.env.NODE_FUNCTION_ALLOW_BUILTIN.split(','); + vmRequire.builtin = process.env.NODE_FUNCTION_ALLOW_BUILTIN.split(','); } if (process.env.NODE_FUNCTION_ALLOW_EXTERNAL) { - options.require.external = { + vmRequire.external = { modules: process.env.NODE_FUNCTION_ALLOW_EXTERNAL.split(','), + transitive: false, }; } - const vm = new NodeVM(options); + const vm = new NodeVM(options as unknown as NodeVMOptions); if (mode === 'manual') { vm.on('console.log', this.sendMessageToUI); diff --git a/packages/nodes-base/nodes/GetResponse/GenericFunctions.ts b/packages/nodes-base/nodes/GetResponse/GenericFunctions.ts index 4f7e309485..960277615d 100644 --- a/packages/nodes-base/nodes/GetResponse/GenericFunctions.ts +++ b/packages/nodes-base/nodes/GetResponse/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function getresponseApiRequest( method, body, qs, - uri: uri || `https://api.getresponse.com/v3${resource}`, + uri: uri ?? `https://api.getresponse.com/v3${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/GetResponse/GetResponse.node.ts b/packages/nodes-base/nodes/GetResponse/GetResponse.node.ts index 763e09fc7c..d2ebbbca8f 100644 --- a/packages/nodes-base/nodes/GetResponse/GetResponse.node.ts +++ b/packages/nodes-base/nodes/GetResponse/GetResponse.node.ts @@ -244,7 +244,7 @@ export class GetResponse implements INodeType { } if (qs.sortBy) { - qs[`sort[${qs.sortBy}]`] = qs.sortOrder || 'ASC'; + qs[`sort[${qs.sortBy}]`] = qs.sortOrder ?? 'ASC'; } if (qs.exactMatch === true) { diff --git a/packages/nodes-base/nodes/Ghost/GenericFunctions.ts b/packages/nodes-base/nodes/Ghost/GenericFunctions.ts index e46442ac1a..f943d1458c 100644 --- a/packages/nodes-base/nodes/Ghost/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Ghost/GenericFunctions.ts @@ -37,7 +37,7 @@ export async function ghostApiRequest( const options: OptionsWithUri = { method, qs: query, - uri: uri || `${credentials.url}/ghost/api/${version}${endpoint}`, + uri: uri ?? `${credentials.url}/ghost/api/${version}${endpoint}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/Ghost/Ghost.node.ts b/packages/nodes-base/nodes/Ghost/Ghost.node.ts index b4c22b5fbf..0ba954b83a 100644 --- a/packages/nodes-base/nodes/Ghost/Ghost.node.ts +++ b/packages/nodes-base/nodes/Ghost/Ghost.node.ts @@ -291,7 +291,7 @@ export class Ghost implements INodeType { const post: IDataObject = {}; if (contentFormat === 'html') { - post.html = updateFields.content || ''; + post.html = updateFields.content ?? ''; qs.source = 'html'; delete updateFields.content; } else { diff --git a/packages/nodes-base/nodes/GoToWebinar/GenericFunctions.ts b/packages/nodes-base/nodes/GoToWebinar/GenericFunctions.ts index f2ce1127ce..29922178da 100644 --- a/packages/nodes-base/nodes/GoToWebinar/GenericFunctions.ts +++ b/packages/nodes-base/nodes/GoToWebinar/GenericFunctions.ts @@ -13,6 +13,14 @@ import moment from 'moment'; import * as losslessJSON from 'lossless-json'; +function convertLosslessNumber(key: any, value: any) { + if (value?.isLosslessNumber) { + return value.toString(); + } else { + return value; + } +} + /** * Make an authenticated API request to GoToWebinar. */ @@ -275,11 +283,3 @@ export async function loadRegistranMultiChoiceQuestions(this: ILoadOptionsFuncti return returnData; } - -function convertLosslessNumber(key: any, value: any) { - if (value?.isLosslessNumber) { - return value.toString(); - } else { - return value; - } -} diff --git a/packages/nodes-base/nodes/Google/Ads/CampaignDescription.ts b/packages/nodes-base/nodes/Google/Ads/CampaignDescription.ts index 156e26134f..1a1eb4a4aa 100644 --- a/packages/nodes-base/nodes/Google/Ads/CampaignDescription.ts +++ b/packages/nodes-base/nodes/Google/Ads/CampaignDescription.ts @@ -6,6 +6,26 @@ import { INodeProperties, } from 'n8n-workflow'; +async function processCampaignSearchResponse( + this: IExecuteSingleFunctions, + _inputData: INodeExecutionData[], + responseData: IN8nHttpFullResponse, +): Promise { + const results = (responseData.body as IDataObject).results as GoogleAdsCampaignElement; + + return Promise.resolve( + results.map((result) => { + return { + json: { + ...result.campaign, + ...result.metrics, + ...result.campaignBudget, + }, + }; + }), + ); +} + export const campaignOperations: INodeProperties[] = [ { displayName: 'Operation', @@ -263,26 +283,6 @@ export const campaignFields: INodeProperties[] = [ }, ]; -async function processCampaignSearchResponse( - this: IExecuteSingleFunctions, - _inputData: INodeExecutionData[], - responseData: IN8nHttpFullResponse, -): Promise { - const results = (responseData.body as IDataObject).results as GoogleAdsCampaignElement; - - return Promise.resolve( - results.map((result) => { - return { - json: { - ...result.campaign, - ...result.metrics, - ...result.campaignBudget, - }, - }; - }), - ); -} - type GoogleAdsCampaignElement = [ { campaign: object; diff --git a/packages/nodes-base/nodes/Google/Analytics/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Analytics/GenericFunctions.ts index 04a73fe63e..42107df6d7 100644 --- a/packages/nodes-base/nodes/Google/Analytics/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Analytics/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://analyticsreporting.googleapis.com${endpoint}`, + uri: uri ?? `https://analyticsreporting.googleapis.com${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/Google/BigQuery/GenericFunctions.ts b/packages/nodes-base/nodes/Google/BigQuery/GenericFunctions.ts index ee63dccec0..0f7f557cf8 100644 --- a/packages/nodes-base/nodes/Google/BigQuery/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/BigQuery/GenericFunctions.ts @@ -8,6 +8,54 @@ import moment from 'moment-timezone'; import * as jwt from 'jsonwebtoken'; +async function getAccessToken( + this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + credentials: IDataObject, +): Promise { + //https://developers.google.com/identity/protocols/oauth2/service-account#httprest + + const privateKey = (credentials.privateKey as string).replace(/\\n/g, '\n').trim(); + + const scopes = ['https://www.googleapis.com/auth/bigquery']; + + const now = moment().unix(); + + const signature = jwt.sign( + { + iss: credentials.email as string, + sub: credentials.delegatedEmail ?? (credentials.email as string), + scope: scopes.join(' '), + aud: 'https://oauth2.googleapis.com/token', + iat: now, + exp: now + 3600, + }, + privateKey, + { + algorithm: 'RS256', + header: { + kid: privateKey, + typ: 'JWT', + alg: 'RS256', + }, + }, + ); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: signature, + }, + uri: 'https://oauth2.googleapis.com/token', + json: true, + }; + + return this.helpers.request(options); +} + export async function googleApiRequest( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -31,7 +79,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://bigquery.googleapis.com/bigquery${resource}`, + uri: uri ?? `https://bigquery.googleapis.com/bigquery${resource}`, json: true, }; try { @@ -54,7 +102,6 @@ export async function googleApiRequest( options.headers!.Authorization = `Bearer ${access_token}`; return await this.helpers.request(options); } else { - //@ts-ignore return await this.helpers.requestOAuth2.call(this, 'googleBigQueryOAuth2Api', options); } } catch (error) { @@ -89,54 +136,6 @@ export async function googleApiRequestAllItems( return returnData; } -async function getAccessToken( - this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, - credentials: IDataObject, -): Promise { - //https://developers.google.com/identity/protocols/oauth2/service-account#httprest - - const privateKey = (credentials.privateKey as string).replace(/\\n/g, '\n').trim(); - - const scopes = ['https://www.googleapis.com/auth/bigquery']; - - const now = moment().unix(); - - const signature = jwt.sign( - { - iss: credentials.email as string, - sub: credentials.delegatedEmail || (credentials.email as string), - scope: scopes.join(' '), - aud: 'https://oauth2.googleapis.com/token', - iat: now, - exp: now + 3600, - }, - privateKey, - { - algorithm: 'RS256', - header: { - kid: privateKey, - typ: 'JWT', - alg: 'RS256', - }, - }, - ); - - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - form: { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: signature, - }, - uri: 'https://oauth2.googleapis.com/token', - json: true, - }; - - return this.helpers.request(options); -} - export function simplify(rows: IDataObject[], fields: string[]) { const results = []; for (const row of rows) { diff --git a/packages/nodes-base/nodes/Google/Books/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Books/GenericFunctions.ts index 1237d95b6d..3236564fb7 100644 --- a/packages/nodes-base/nodes/Google/Books/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Books/GenericFunctions.ts @@ -15,6 +15,55 @@ interface IGoogleAuthCredentials { privateKey: string; } +async function getAccessToken( + this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + credentials: IGoogleAuthCredentials, +): Promise { + //https://developers.google.com/identity/protocols/oauth2/service-account#httprest + + const scopes = ['https://www.googleapis.com/auth/books']; + + const now = moment().unix(); + + credentials.email = credentials.email.trim(); + const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); + + const signature = jwt.sign( + { + iss: credentials.email, + sub: credentials.delegatedEmail ?? credentials.email, + scope: scopes.join(' '), + aud: 'https://oauth2.googleapis.com/token', + iat: now, + exp: now + 3600, + }, + privateKey, + { + algorithm: 'RS256', + header: { + kid: privateKey, + typ: 'JWT', + alg: 'RS256', + }, + }, + ); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: signature, + }, + uri: 'https://oauth2.googleapis.com/token', + json: true, + }; + + return this.helpers.request(options); +} + export async function googleApiRequest( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -37,7 +86,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://www.googleapis.com/books/${resource}`, + uri: uri ?? `https://www.googleapis.com/books/${resource}`, json: true, }; try { @@ -60,10 +109,9 @@ export async function googleApiRequest( ); options.headers!.Authorization = `Bearer ${access_token}`; - //@ts-ignore + return await this.helpers.request(options); } else { - //@ts-ignore return await this.helpers.requestOAuth2.call(this, 'googleBooksOAuth2Api', options); } } catch (error) { @@ -96,52 +144,3 @@ export async function googleApiRequestAllItems( return returnData; } - -async function getAccessToken( - this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, - credentials: IGoogleAuthCredentials, -): Promise { - //https://developers.google.com/identity/protocols/oauth2/service-account#httprest - - const scopes = ['https://www.googleapis.com/auth/books']; - - const now = moment().unix(); - - credentials.email = credentials.email.trim(); - const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); - - const signature = jwt.sign( - { - iss: credentials.email, - sub: credentials.delegatedEmail || credentials.email, - scope: scopes.join(' '), - aud: 'https://oauth2.googleapis.com/token', - iat: now, - exp: now + 3600, - }, - privateKey, - { - algorithm: 'RS256', - header: { - kid: privateKey, - typ: 'JWT', - alg: 'RS256', - }, - }, - ); - - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - form: { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: signature, - }, - uri: 'https://oauth2.googleapis.com/token', - json: true, - }; - - return this.helpers.request(options); -} diff --git a/packages/nodes-base/nodes/Google/Calendar/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Calendar/GenericFunctions.ts index 001c2653e1..69cd2f91f1 100644 --- a/packages/nodes-base/nodes/Google/Calendar/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Calendar/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://www.googleapis.com${resource}`, + uri: uri ?? `https://www.googleapis.com${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts index 824918654c..f94b2d7768 100644 --- a/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts +++ b/packages/nodes-base/nodes/Google/Calendar/GoogleCalendar.node.ts @@ -144,7 +144,7 @@ export class GoogleCalendar implements INodeType { const timeMin = this.getNodeParameter('timeMin', i) as string; const timeMax = this.getNodeParameter('timeMax', i) as string; const options = this.getNodeParameter('options', i); - const outputFormat = options.outputFormat || 'availability'; + const outputFormat = options.outputFormat ?? 'availability'; const tz = this.getNodeParameter('options.timezone', i, '', { extractValue: true, }) as string; diff --git a/packages/nodes-base/nodes/Google/Chat/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Chat/GenericFunctions.ts index 7c5d7e2dce..5631e1e855 100644 --- a/packages/nodes-base/nodes/Google/Chat/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Chat/GenericFunctions.ts @@ -21,6 +21,59 @@ interface IGoogleAuthCredentials { privateKey: string; } +export async function getAccessToken( + this: + | IExecuteFunctions + | IExecuteSingleFunctions + | ILoadOptionsFunctions + | ICredentialTestFunctions, + credentials: IGoogleAuthCredentials, +): Promise { + //https://developers.google.com/identity/protocols/oauth2/service-account#httprest + + const scopes = ['https://www.googleapis.com/auth/chat.bot']; + + const now = moment().unix(); + + credentials.email = credentials.email.trim(); + const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); + + const signature = jwt.sign( + { + iss: credentials.email, + sub: credentials.delegatedEmail ?? credentials.email, + scope: scopes.join(' '), + aud: 'https://oauth2.googleapis.com/token', + iat: now, + exp: now + 3600, + }, + privateKey, + { + algorithm: 'RS256', + header: { + kid: privateKey, + typ: 'JWT', + alg: 'RS256', + }, + }, + ); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: signature, + }, + uri: 'https://oauth2.googleapis.com/token', + json: true, + }; + + return this.helpers.request(options); +} + export async function googleApiRequest( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -39,7 +92,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://chat.googleapis.com${resource}`, + uri: uri ?? `https://chat.googleapis.com${resource}`, json: true, }; @@ -54,7 +107,6 @@ export async function googleApiRequest( let responseData: IDataObject | undefined; try { if (noCredentials) { - //@ts-ignore responseData = await this.helpers.request(options); } else { const credentials = await this.getCredentials('googleApi'); @@ -64,7 +116,6 @@ export async function googleApiRequest( credentials as unknown as IGoogleAuthCredentials, ); options.headers!.Authorization = `Bearer ${access_token}`; - //@ts-ignore responseData = await this.helpers.request(options); } } catch (error) { @@ -104,59 +155,6 @@ export async function googleApiRequestAllItems( return returnData; } -export async function getAccessToken( - this: - | IExecuteFunctions - | IExecuteSingleFunctions - | ILoadOptionsFunctions - | ICredentialTestFunctions, - credentials: IGoogleAuthCredentials, -): Promise { - //https://developers.google.com/identity/protocols/oauth2/service-account#httprest - - const scopes = ['https://www.googleapis.com/auth/chat.bot']; - - const now = moment().unix(); - - credentials.email = credentials.email.trim(); - const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); - - const signature = jwt.sign( - { - iss: credentials.email, - sub: credentials.delegatedEmail || credentials.email, - scope: scopes.join(' '), - aud: 'https://oauth2.googleapis.com/token', - iat: now, - exp: now + 3600, - }, - privateKey, - { - algorithm: 'RS256', - header: { - kid: privateKey, - typ: 'JWT', - alg: 'RS256', - }, - }, - ); - - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - form: { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: signature, - }, - uri: 'https://oauth2.googleapis.com/token', - json: true, - }; - - return this.helpers.request(options); -} - export function validateJSON(json: string | undefined): any { let result; try { diff --git a/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GenericFunctions.ts b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GenericFunctions.ts index 8b67ddf83c..6c9896acee 100644 --- a/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://language.googleapis.com${endpoint}`, + uri: uri ?? `https://language.googleapis.com${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.ts b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.ts index e0b3e41c52..118b57350c 100644 --- a/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.ts +++ b/packages/nodes-base/nodes/Google/CloudNaturalLanguage/GoogleCloudNaturalLanguage.node.ts @@ -262,8 +262,8 @@ export class GoogleCloudNaturalLanguage implements INodeType { if (operation === 'analyzeSentiment') { const source = this.getNodeParameter('source', i) as string; const options = this.getNodeParameter('options', i); - const encodingType = (options.encodingType as string | undefined) || 'UTF16'; - const documentType = (options.documentType as string | undefined) || 'PLAIN_TEXT'; + const encodingType = (options.encodingType as string | undefined) ?? 'UTF16'; + const documentType = (options.documentType as string | undefined) ?? 'PLAIN_TEXT'; const body: IData = { document: { diff --git a/packages/nodes-base/nodes/Google/Contacts/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Contacts/GenericFunctions.ts index a269b0282d..96a8e0ad61 100644 --- a/packages/nodes-base/nodes/Google/Contacts/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Contacts/GenericFunctions.ts @@ -21,7 +21,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://people.googleapis.com/v1${resource}`, + uri: uri ?? `https://people.googleapis.com/v1${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Google/Docs/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Docs/GenericFunctions.ts index 8a54fa9c2d..a4023cb3a2 100644 --- a/packages/nodes-base/nodes/Google/Docs/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Docs/GenericFunctions.ts @@ -15,79 +15,6 @@ interface IGoogleAuthCredentials { privateKey: string; } -export async function googleApiRequest( - this: IExecuteFunctions | ILoadOptionsFunctions, - method: string, - endpoint: string, - body: IDataObject = {}, - qs?: IDataObject, - uri?: string, -) { - const authenticationMethod = this.getNodeParameter( - 'authentication', - 0, - 'serviceAccount', - ) as string; - - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/json', - }, - method, - body, - qs, - uri: uri || `https://docs.googleapis.com/v1${endpoint}`, - json: true, - }; - - if (!Object.keys(body).length) { - delete options.body; - } - try { - if (authenticationMethod === 'serviceAccount') { - const credentials = await this.getCredentials('googleApi'); - - const { access_token } = await getAccessToken.call( - this, - credentials as unknown as IGoogleAuthCredentials, - ); - - options.headers!.Authorization = `Bearer ${access_token}`; - return await this.helpers.request(options); - } else { - //@ts-ignore - return await this.helpers.requestOAuth2.call(this, 'googleDocsOAuth2Api', options); - } - } catch (error) { - throw new NodeApiError(this.getNode(), error); - } -} - -export async function googleApiRequestAllItems( - this: IExecuteFunctions | ILoadOptionsFunctions, - propertyName: string, - method: string, - endpoint: string, - body: IDataObject = {}, - qs?: IDataObject, - uri?: string, -): Promise { - const returnData: IDataObject[] = []; - - let responseData; - const query: IDataObject = { ...qs }; - query.maxResults = 100; - query.pageSize = 100; - - do { - responseData = await googleApiRequest.call(this, method, endpoint, body, query, uri); - query.pageToken = responseData.nextPageToken; - returnData.push.apply(returnData, responseData[propertyName]); - } while (responseData.nextPageToken !== undefined && responseData.nextPageToken !== ''); - - return returnData; -} - async function getAccessToken( this: IExecuteFunctions | ILoadOptionsFunctions, credentials: IGoogleAuthCredentials, @@ -108,7 +35,7 @@ async function getAccessToken( const signature = jwt.sign( { iss: credentials.email, - sub: credentials.delegatedEmail || credentials.email, + sub: credentials.delegatedEmail ?? credentials.email, scope: scopes.join(' '), aud: 'https://oauth2.googleapis.com/token', iat: now, @@ -141,6 +68,78 @@ async function getAccessToken( return this.helpers.request(options); } +export async function googleApiRequest( + this: IExecuteFunctions | ILoadOptionsFunctions, + method: string, + endpoint: string, + body: IDataObject = {}, + qs?: IDataObject, + uri?: string, +) { + const authenticationMethod = this.getNodeParameter( + 'authentication', + 0, + 'serviceAccount', + ) as string; + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri ?? `https://docs.googleapis.com/v1${endpoint}`, + json: true, + }; + + if (!Object.keys(body).length) { + delete options.body; + } + try { + if (authenticationMethod === 'serviceAccount') { + const credentials = await this.getCredentials('googleApi'); + + const { access_token } = await getAccessToken.call( + this, + credentials as unknown as IGoogleAuthCredentials, + ); + + options.headers!.Authorization = `Bearer ${access_token}`; + return await this.helpers.request(options); + } else { + return await this.helpers.requestOAuth2.call(this, 'googleDocsOAuth2Api', options); + } + } catch (error) { + throw new NodeApiError(this.getNode(), error); + } +} + +export async function googleApiRequestAllItems( + this: IExecuteFunctions | ILoadOptionsFunctions, + propertyName: string, + method: string, + endpoint: string, + body: IDataObject = {}, + qs?: IDataObject, + uri?: string, +): Promise { + const returnData: IDataObject[] = []; + + let responseData; + const query: IDataObject = { ...qs }; + query.maxResults = 100; + query.pageSize = 100; + + do { + responseData = await googleApiRequest.call(this, method, endpoint, body, query, uri); + query.pageToken = responseData.nextPageToken; + returnData.push.apply(returnData, responseData[propertyName]); + } while (responseData.nextPageToken !== undefined && responseData.nextPageToken !== ''); + + return returnData; +} + export const hasKeys = (obj = {}) => Object.keys(obj).length > 0; export const extractID = (url: string) => { const regex = new RegExp('https://docs.google.com/document/d/([a-zA-Z0-9-_]+)/'); diff --git a/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts index f07940c857..02f0948a72 100644 --- a/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Drive/GenericFunctions.ts @@ -15,86 +15,6 @@ interface IGoogleAuthCredentials { privateKey: string; } -export async function googleApiRequest( - this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, - method: string, - resource: string, - - body: any = {}, - qs: IDataObject = {}, - uri?: string, - option: IDataObject = {}, -): Promise { - const authenticationMethod = this.getNodeParameter( - 'authentication', - 0, - 'serviceAccount', - ) as string; - - let options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/json', - }, - method, - body, - qs, - uri: uri || `https://www.googleapis.com${resource}`, - json: true, - }; - - options = Object.assign({}, options, option); - - try { - if (Object.keys(body).length === 0) { - delete options.body; - } - - if (authenticationMethod === 'serviceAccount') { - const credentials = await this.getCredentials('googleApi'); - - const { access_token } = await getAccessToken.call( - this, - credentials as unknown as IGoogleAuthCredentials, - ); - - options.headers!.Authorization = `Bearer ${access_token}`; - return await this.helpers.request(options); - } else { - //@ts-ignore - return await this.helpers.requestOAuth2.call(this, 'googleDriveOAuth2Api', options); - } - } catch (error) { - if (error.code === 'ERR_OSSL_PEM_NO_START_LINE') { - error.statusCode = '401'; - } - - throw new NodeApiError(this.getNode(), error); - } -} - -export async function googleApiRequestAllItems( - this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions, - propertyName: string, - method: string, - endpoint: string, - - body: any = {}, - query: IDataObject = {}, -): Promise { - const returnData: IDataObject[] = []; - - let responseData; - query.maxResults = query.maxResults || 100; - query.pageSize = query.pageSize || 100; - - do { - responseData = await googleApiRequest.call(this, method, endpoint, body, query); - returnData.push.apply(returnData, responseData[propertyName]); - } while (responseData.nextPageToken !== undefined && responseData.nextPageToken !== ''); - - return returnData; -} - async function getAccessToken( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, credentials: IGoogleAuthCredentials, @@ -115,7 +35,7 @@ async function getAccessToken( const signature = jwt.sign( { iss: credentials.email, - sub: credentials.delegatedEmail || credentials.email, + sub: credentials.delegatedEmail ?? credentials.email, scope: scopes.join(' '), aud: 'https://oauth2.googleapis.com/token', iat: now, @@ -148,6 +68,85 @@ async function getAccessToken( return this.helpers.request(options); } +export async function googleApiRequest( + this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, + method: string, + resource: string, + + body: any = {}, + qs: IDataObject = {}, + uri?: string, + option: IDataObject = {}, +): Promise { + const authenticationMethod = this.getNodeParameter( + 'authentication', + 0, + 'serviceAccount', + ) as string; + + let options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri ?? `https://www.googleapis.com${resource}`, + json: true, + }; + + options = Object.assign({}, options, option); + + try { + if (Object.keys(body).length === 0) { + delete options.body; + } + + if (authenticationMethod === 'serviceAccount') { + const credentials = await this.getCredentials('googleApi'); + + const { access_token } = await getAccessToken.call( + this, + credentials as unknown as IGoogleAuthCredentials, + ); + + options.headers!.Authorization = `Bearer ${access_token}`; + return await this.helpers.request(options); + } else { + return await this.helpers.requestOAuth2.call(this, 'googleDriveOAuth2Api', options); + } + } catch (error) { + if (error.code === 'ERR_OSSL_PEM_NO_START_LINE') { + error.statusCode = '401'; + } + + throw new NodeApiError(this.getNode(), error); + } +} + +export async function googleApiRequestAllItems( + this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions, + propertyName: string, + method: string, + endpoint: string, + + body: any = {}, + query: IDataObject = {}, +): Promise { + const returnData: IDataObject[] = []; + + let responseData; + query.maxResults = query.maxResults ?? 100; + query.pageSize = query.pageSize ?? 100; + + do { + responseData = await googleApiRequest.call(this, method, endpoint, body, query); + returnData.push.apply(returnData, responseData[propertyName]); + } while (responseData.nextPageToken !== undefined && responseData.nextPageToken !== ''); + + return returnData; +} + export function extractId(url: string): string { if (url.includes('/d/')) { //https://docs.google.com/document/d/1TUJGUf5HUv9e6MJBzcOsPruxXDeGMnGYTBWfkMagcg4/edit diff --git a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts index 3858c91164..84650dc7f7 100644 --- a/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts +++ b/packages/nodes-base/nodes/Google/Drive/GoogleDrive.node.ts @@ -2589,7 +2589,7 @@ export class GoogleDrive implements INodeType { let offset = 0; for await (const chunk of fileContent) { - const nextOffset = offset + chunk.length; + const nextOffset = offset + Number(chunk.length); try { const response = await this.helpers.httpRequest({ method: 'PUT', @@ -2729,7 +2729,7 @@ export class GoogleDrive implements INodeType { const body = { name, mimeType: 'application/vnd.google-apps.folder', - parents: options.parents || [], + parents: options.parents ?? [], }; const qs = { diff --git a/packages/nodes-base/nodes/Google/Firebase/CloudFirestore/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Firebase/CloudFirestore/GenericFunctions.ts index c105b925d1..509becba32 100644 --- a/packages/nodes-base/nodes/Google/Firebase/CloudFirestore/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Firebase/CloudFirestore/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function googleApiRequest( qsStringifyOptions: { arrayFormat: 'repeat', }, - uri: uri || `https://firestore.googleapis.com/v1/projects${resource}`, + uri: uri ?? `https://firestore.googleapis.com/v1/projects${resource}`, json: true, }; try { @@ -104,20 +104,6 @@ export function jsonToDocument(value: string | number | IDataObject | IDataObjec return {}; } -export function fullDocumentToJson(data: IDataObject): IDataObject { - if (data === undefined) { - return data; - } - - return { - _name: data.name, - _id: data.id, - _createTime: data.createTime, - _updateTime: data.updateTime, - ...documentToJson(data.fields as IDataObject), - }; -} - export function documentToJson(fields: IDataObject): IDataObject { if (fields === undefined) return {}; const result = {}; @@ -163,3 +149,17 @@ export function documentToJson(fields: IDataObject): IDataObject { } return result; } + +export function fullDocumentToJson(data: IDataObject): IDataObject { + if (data === undefined) { + return data; + } + + return { + _name: data.name, + _id: data.id, + _createTime: data.createTime, + _updateTime: data.updateTime, + ...documentToJson(data.fields as IDataObject), + }; +} diff --git a/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/GenericFunctions.ts index 80b4957843..f1ed51a2d2 100644 --- a/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Firebase/RealtimeDatabase/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function googleApiRequest( method, body, qs, - url: uri || `https://${projectId}.${region}/${resource}.json`, + url: uri ?? `https://${projectId}.${region}/${resource}.json`, json: true, }; diff --git a/packages/nodes-base/nodes/Google/GSuiteAdmin/GenericFunctions.ts b/packages/nodes-base/nodes/Google/GSuiteAdmin/GenericFunctions.ts index d3ab4f0475..e59a3c991a 100644 --- a/packages/nodes-base/nodes/Google/GSuiteAdmin/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/GSuiteAdmin/GenericFunctions.ts @@ -21,7 +21,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://www.googleapis.com/admin${resource}`, + uri: uri ?? `https://www.googleapis.com/admin${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts index 769d5d9ec5..0b8a93121d 100644 --- a/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Gmail/GenericFunctions.ts @@ -41,7 +41,63 @@ export interface IAttachments { content: string; } -const mailComposer = require('nodemailer/lib/mail-composer'); +import MailComposer from 'nodemailer/lib/mail-composer'; + +async function getAccessToken( + this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, + credentials: ICredentialDataDecryptedObject, +): Promise { + //https://developers.google.com/identity/protocols/oauth2/service-account#httprest + + const scopes = [ + 'https://www.googleapis.com/auth/gmail.labels', + 'https://www.googleapis.com/auth/gmail.addons.current.action.compose', + 'https://www.googleapis.com/auth/gmail.addons.current.message.action', + 'https://mail.google.com/', + 'https://www.googleapis.com/auth/gmail.modify', + 'https://www.googleapis.com/auth/gmail.compose', + ]; + + const now = moment().unix(); + + credentials.email = (credentials.email as string).trim(); + const privateKey = (credentials.privateKey as string).replace(/\\n/g, '\n').trim(); + + const signature = jwt.sign( + { + iss: credentials.email, + sub: credentials.delegatedEmail || credentials.email, + scope: scopes.join(' '), + aud: 'https://oauth2.googleapis.com/token', + iat: now, + exp: now + 3600, + }, + privateKey, + { + algorithm: 'RS256', + header: { + kid: privateKey, + typ: 'JWT', + alg: 'RS256', + }, + }, + ); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: signature, + }, + uri: 'https://oauth2.googleapis.com/token', + json: true, + }; + + return this.helpers.request(options); +} export async function googleApiRequest( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, @@ -60,7 +116,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://www.googleapis.com${endpoint}`, + uri: uri ?? `https://www.googleapis.com${endpoint}`, qsStringifyOptions: { arrayFormat: 'repeat', }, @@ -250,12 +306,12 @@ export async function encodeEmail(email: IEmail) { mailOptions.attachments = attachments; } - const mail = new mailComposer(mailOptions).compile(); + const mail = new MailComposer(mailOptions).compile(); // by default the bcc headers are deleted when the mail is built. - // So add keepBcc flag to averride such behaviour. Only works when + // So add keepBcc flag to override such behaviour. Only works when // the flag is set after the compilation. - //https://nodemailer.com/extras/mailcomposer/#bcc + // @ts-expect-error - https://nodemailer.com/extras/mailcomposer/#bcc mail.keepBcc = true; const mailBody = await mail.build(); @@ -294,62 +350,6 @@ export function extractEmail(s: string) { return s; } -async function getAccessToken( - this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, - credentials: ICredentialDataDecryptedObject, -): Promise { - //https://developers.google.com/identity/protocols/oauth2/service-account#httprest - - const scopes = [ - 'https://www.googleapis.com/auth/gmail.labels', - 'https://www.googleapis.com/auth/gmail.addons.current.action.compose', - 'https://www.googleapis.com/auth/gmail.addons.current.message.action', - 'https://mail.google.com/', - 'https://www.googleapis.com/auth/gmail.modify', - 'https://www.googleapis.com/auth/gmail.compose', - ]; - - const now = moment().unix(); - - credentials.email = (credentials.email as string).trim(); - const privateKey = (credentials.privateKey as string).replace(/\\n/g, '\n').trim(); - - const signature = jwt.sign( - { - iss: credentials.email, - sub: credentials.delegatedEmail || credentials.email, - scope: scopes.join(' '), - aud: 'https://oauth2.googleapis.com/token', - iat: now, - exp: now + 3600, - }, - privateKey, - { - algorithm: 'RS256', - header: { - kid: privateKey, - typ: 'JWT', - alg: 'RS256', - }, - }, - ); - - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - form: { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: signature, - }, - uri: 'https://oauth2.googleapis.com/token', - json: true, - }; - - return this.helpers.request(options); -} - export function prepareQuery( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IPollFunctions, fields: IDataObject, @@ -528,7 +528,7 @@ export async function prepareEmailAttachments( } attachmentsList.push({ - name: binaryData.fileName || 'unknown', + name: binaryData.fileName ?? 'unknown', content: binaryDataBuffer, type: binaryData.mimeType, }); diff --git a/packages/nodes-base/nodes/Google/Gmail/GmailTrigger.node.ts b/packages/nodes-base/nodes/Google/Gmail/GmailTrigger.node.ts index aaa51c90dc..8b3add99a0 100644 --- a/packages/nodes-base/nodes/Google/Gmail/GmailTrigger.node.ts +++ b/packages/nodes-base/nodes/Google/Gmail/GmailTrigger.node.ts @@ -190,7 +190,7 @@ export class GmailTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); let responseData; - const now = Math.floor(DateTime.now().toSeconds()) + ''; + const now = Math.floor(DateTime.now().toSeconds()).toString(); const startDate = (webhookData.lastTimeChecked as string) || +now; const endDate = +now; diff --git a/packages/nodes-base/nodes/Google/Gmail/v1/GmailV1.node.ts b/packages/nodes-base/nodes/Google/Gmail/v1/GmailV1.node.ts index 8ef63b1e24..356ca646fe 100644 --- a/packages/nodes-base/nodes/Google/Gmail/v1/GmailV1.node.ts +++ b/packages/nodes-base/nodes/Google/Gmail/v1/GmailV1.node.ts @@ -324,7 +324,7 @@ export class GmailV1 implements INodeType { binaryProperty, ); attachmentsBinary.push({ - name: binaryData.fileName || 'unknown', + name: binaryData.fileName ?? 'unknown', content: binaryDataBuffer, type: binaryData.mimeType, }); @@ -414,7 +414,7 @@ export class GmailV1 implements INodeType { binaryProperty, ); attachmentsBinary.push({ - name: binaryData.fileName || 'unknown', + name: binaryData.fileName ?? 'unknown', content: binaryDataBuffer, type: binaryData.mimeType, }); @@ -489,7 +489,7 @@ export class GmailV1 implements INodeType { const id = this.getNodeParameter('messageId', i); const additionalFields = this.getNodeParameter('additionalFields', i); - const format = additionalFields.format || 'resolved'; + const format = additionalFields.format ?? 'resolved'; if (format === 'resolved') { qs.format = 'raw'; @@ -557,7 +557,7 @@ export class GmailV1 implements INodeType { responseData = []; } - const format = additionalFields.format || 'resolved'; + const format = additionalFields.format ?? 'resolved'; if (format !== 'ids') { if (format === 'resolved') { @@ -658,7 +658,7 @@ export class GmailV1 implements INodeType { binaryProperty, ); attachmentsBinary.push({ - name: binaryData.fileName || 'unknown', + name: binaryData.fileName ?? 'unknown', content: binaryDataBuffer, type: binaryData.mimeType, }); @@ -707,7 +707,7 @@ export class GmailV1 implements INodeType { const id = this.getNodeParameter('messageId', i); const additionalFields = this.getNodeParameter('additionalFields', i); - const format = additionalFields.format || 'resolved'; + const format = additionalFields.format ?? 'resolved'; if (format === 'resolved') { qs.format = 'raw'; @@ -785,7 +785,7 @@ export class GmailV1 implements INodeType { responseData = []; } - const format = additionalFields.format || 'resolved'; + const format = additionalFields.format ?? 'resolved'; if (format !== 'ids') { if (format === 'resolved') { diff --git a/packages/nodes-base/nodes/Google/Gmail/v2/GmailV2.node.ts b/packages/nodes-base/nodes/Google/Gmail/v2/GmailV2.node.ts index dbf135e1d4..846f2b34e3 100644 --- a/packages/nodes-base/nodes/Google/Gmail/v2/GmailV2.node.ts +++ b/packages/nodes-base/nodes/Google/Gmail/v2/GmailV2.node.ts @@ -678,7 +678,7 @@ export class GmailV2 implements INodeType { const endpoint = `/gmail/v1/users/me/threads/${id}`; const options = this.getNodeParameter('options', i); - const onlyMessages = options.returnOnlyMessages || false; + const onlyMessages = options.returnOnlyMessages ?? false; const qs: IDataObject = {}; const simple = this.getNodeParameter('simple', i) as boolean; diff --git a/packages/nodes-base/nodes/Google/Perspective/GooglePerspective.node.ts b/packages/nodes-base/nodes/Google/Perspective/GooglePerspective.node.ts index 38589a1e84..07699ecbda 100644 --- a/packages/nodes-base/nodes/Google/Perspective/GooglePerspective.node.ts +++ b/packages/nodes-base/nodes/Google/Perspective/GooglePerspective.node.ts @@ -13,7 +13,7 @@ import { AttributesValuesUi, CommentAnalyzeBody, Language, RequestedAttributes } import { googleApiRequest } from './GenericFunctions'; -const ISO6391 = require('iso-639-1'); +import ISO6391 from 'iso-639-1'; export class GooglePerspective implements INodeType { description: INodeTypeDescription = { diff --git a/packages/nodes-base/nodes/Google/Sheet/v1/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Sheet/v1/GenericFunctions.ts index 8a789cf907..ffd56ed5cf 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v1/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v1/GenericFunctions.ts @@ -15,6 +15,63 @@ export interface IGoogleAuthCredentials { privateKey: string; } +export async function getAccessToken( + this: + | IExecuteFunctions + | IExecuteSingleFunctions + | ILoadOptionsFunctions + | ICredentialTestFunctions, + credentials: IGoogleAuthCredentials, +): Promise { + //https://developers.google.com/identity/protocols/oauth2/service-account#httprest + + const scopes = [ + 'https://www.googleapis.com/auth/drive', + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/spreadsheets', + ]; + + const now = moment().unix(); + + credentials.email = credentials.email.trim(); + const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); + + const signature = jwt.sign( + { + iss: credentials.email, + sub: credentials.delegatedEmail ?? credentials.email, + scope: scopes.join(' '), + aud: 'https://oauth2.googleapis.com/token', + iat: now, + exp: now + 3600, + }, + privateKey, + { + algorithm: 'RS256', + header: { + kid: privateKey, + typ: 'JWT', + alg: 'RS256', + }, + }, + ); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: signature, + }, + uri: 'https://oauth2.googleapis.com/token', + json: true, + }; + + return this.helpers.request(options); +} + export async function googleApiRequest( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -37,7 +94,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://sheets.googleapis.com${resource}`, + uri: uri ?? `https://sheets.googleapis.com${resource}`, json: true, }; try { @@ -57,10 +114,8 @@ export async function googleApiRequest( ); options.headers!.Authorization = `Bearer ${access_token}`; - //@ts-ignore return await this.helpers.request(options); } else { - //@ts-ignore return await this.helpers.requestOAuth2.call(this, 'googleSheetsOAuth2Api', options); } } catch (error) { @@ -95,68 +150,12 @@ export async function googleApiRequestAllItems( return returnData; } -export async function getAccessToken( - this: - | IExecuteFunctions - | IExecuteSingleFunctions - | ILoadOptionsFunctions - | ICredentialTestFunctions, - credentials: IGoogleAuthCredentials, -): Promise { - //https://developers.google.com/identity/protocols/oauth2/service-account#httprest - - const scopes = [ - 'https://www.googleapis.com/auth/drive', - 'https://www.googleapis.com/auth/drive.file', - 'https://www.googleapis.com/auth/spreadsheets', - ]; - - const now = moment().unix(); - - credentials.email = credentials.email.trim(); - const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); - - const signature = jwt.sign( - { - iss: credentials.email, - sub: credentials.delegatedEmail || credentials.email, - scope: scopes.join(' '), - aud: 'https://oauth2.googleapis.com/token', - iat: now, - exp: now + 3600, - }, - privateKey, - { - algorithm: 'RS256', - header: { - kid: privateKey, - typ: 'JWT', - alg: 'RS256', - }, - }, - ); - - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - form: { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: signature, - }, - uri: 'https://oauth2.googleapis.com/token', - json: true, - }; - - return this.helpers.request(options); -} - // Hex to RGB export function hexToRgb(hex: string) { // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandRegex, (m, r, g, b) => { + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands return r + r + g + g + b + b; }); diff --git a/packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheetsV1.node.ts b/packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheetsV1.node.ts index 811982afee..1a3e9aec09 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheetsV1.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v1/GoogleSheetsV1.node.ts @@ -117,8 +117,8 @@ export class GoogleSheetsV1 implements INodeType { const options = this.getNodeParameter('options', 0, {}); - const valueInputMode = (options.valueInputMode || 'RAW') as ValueInputOption; - const valueRenderMode = (options.valueRenderMode || 'UNFORMATTED_VALUE') as ValueRenderOption; + const valueInputMode = (options.valueInputMode ?? 'RAW') as ValueInputOption; + const valueRenderMode = (options.valueRenderMode ?? 'UNFORMATTED_VALUE') as ValueRenderOption; if (operation === 'append') { // ---------------------------------- @@ -134,7 +134,7 @@ export class GoogleSheetsV1 implements INodeType { setData.push(item.json); }); - const usePathForKeyRow = (options.usePathForKeyRow || false) as boolean; + const usePathForKeyRow = (options.usePathForKeyRow ?? false) as boolean; // Convert data into array format const _data = await sheet.appendSheetData( diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts index 04620109d6..6ad153d6c1 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts @@ -172,7 +172,7 @@ export async function execute( const options = this.getNodeParameter('options', 0, {}); - const valueRenderMode = (options.valueRenderMode || 'UNFORMATTED_VALUE') as ValueRenderOption; + const valueRenderMode = (options.valueRenderMode ?? 'UNFORMATTED_VALUE') as ValueRenderOption; const locationDefineOption = (options.locationDefine as IDataObject)?.values as IDataObject; diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts index 14731532b5..5e4b5fe73c 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts @@ -124,9 +124,9 @@ export async function execute( const range = getRangeString(sheetName, dataLocationOnSheetOptions); - const valueRenderMode = (outputFormattingOption.general || + const valueRenderMode = (outputFormattingOption.general ?? 'UNFORMATTED_VALUE') as ValueRenderOption; - const dateTimeRenderOption = (outputFormattingOption.date || 'FORMATTED_STRING') as string; + const dateTimeRenderOption = (outputFormattingOption.date ?? 'FORMATTED_STRING') as string; const sheetData = (await sheet.getData( range, diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts index 17a1cbe225..2a498023bc 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts @@ -171,7 +171,7 @@ export async function execute( const options = this.getNodeParameter('options', 0, {}); - const valueRenderMode = (options.valueRenderMode || 'UNFORMATTED_VALUE') as ValueRenderOption; + const valueRenderMode = (options.valueRenderMode ?? 'UNFORMATTED_VALUE') as ValueRenderOption; const locationDefineOptions = (options.locationDefine as IDataObject)?.values as IDataObject; diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts index a3e76008ee..72f9f9487c 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts @@ -252,7 +252,7 @@ export class GoogleSheet { // ); const lastRowWithData = - lastRow || + lastRow ?? (((await this.getData(range, 'UNFORMATTED_VALUE')) as string[][]) || []).length + 1; const response = await this.updateRows( @@ -400,10 +400,10 @@ export class GoogleSheet { columnValuesList = sheetData.slice(dataStartRowIndex - 1).map((row) => row[keyIndex]); } else { const decodedRange = this.getDecodedSheetRange(range); - const startRowIndex = decodedRange.start?.row || dataStartRowIndex; - const endRowIndex = decodedRange.end?.row || ''; + const startRowIndex = decodedRange.start?.row ?? dataStartRowIndex; + const endRowIndex = decodedRange.end?.row ?? ''; - const keyColumn = this.getColumnWithOffset(decodedRange.start?.column || 'A', keyIndex); + const keyColumn = this.getColumnWithOffset(decodedRange.start?.column ?? 'A', keyIndex); const keyColumnRange = `${decodedRange.name}!${keyColumn}${startRowIndex}:${keyColumn}${endRowIndex}`; columnValuesList = await this.getData(keyColumnRange, valueRenderMode); } @@ -443,9 +443,9 @@ export class GoogleSheet { ) { const decodedRange = this.getDecodedSheetRange(range); // prettier-ignore - const keyRowRange = `${decodedRange.name}!${decodedRange.start?.column || ''}${keyRowIndex + 1}:${decodedRange.end?.column || ''}${keyRowIndex + 1}`; + const keyRowRange = `${decodedRange.name}!${decodedRange.start?.column ?? ''}${keyRowIndex + 1}:${decodedRange.end?.column ?? ''}${keyRowIndex + 1}`; - const sheetDatakeyRow = columnNamesList || (await this.getData(keyRowRange, valueRenderMode)); + const sheetDatakeyRow = columnNamesList ?? (await this.getData(keyRowRange, valueRenderMode)); if (sheetDatakeyRow === undefined) { throw new NodeOperationError( @@ -466,7 +466,7 @@ export class GoogleSheet { } const columnValues: Array = - columnValuesList || + columnValuesList ?? (await this.getColumnValues(range, keyIndex, dataStartRowIndex, valueRenderMode)); const updateData: ISheetUpdateData[] = []; @@ -524,7 +524,7 @@ export class GoogleSheet { // Property exists so add it to the data to update // Get the column name in which the property data can be found const columnToUpdate = this.getColumnWithOffset( - decodedRange.start?.column || 'A', + decodedRange.start?.column ?? 'A', columnNames.indexOf(name), ); @@ -637,7 +637,7 @@ export class GoogleSheet { const decodedRange = this.getDecodedSheetRange(range); const columnNamesRow = - columnNamesList || + columnNamesList ?? (await this.getData( `${decodedRange.name}!${keyRowIndex}:${keyRowIndex}`, 'UNFORMATTED_VALUE', @@ -701,7 +701,7 @@ export class GoogleSheet { } private splitCellRange(cell: string, range: string): SheetCellDecoded { - const cellData = cell.match(/([a-zA-Z]{1,10})([0-9]{0,10})/) || []; + const cellData = cell.match(/([a-zA-Z]{1,10})([0-9]{0,10})/) ?? []; if (cellData === null || cellData.length !== 3) { throw new NodeOperationError( diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts index cd61c5a918..e480c3a8b1 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheets.utils.ts @@ -67,6 +67,7 @@ export function hexToRgb(hex: string) { // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandRegex, (m, r, g, b) => { + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands return r + r + g + g + b + b; }); diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/transport/index.ts b/packages/nodes-base/nodes/Google/Sheet/v2/transport/index.ts index a1e8152fd4..3f91e15871 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/transport/index.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/transport/index.ts @@ -11,6 +11,63 @@ export interface IGoogleAuthCredentials { privateKey: string; } +export async function getAccessToken( + this: + | IExecuteFunctions + | IExecuteSingleFunctions + | ILoadOptionsFunctions + | ICredentialTestFunctions, + credentials: IGoogleAuthCredentials, +): Promise { + //https://developers.google.com/identity/protocols/oauth2/service-account#httprest + + const scopes = [ + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/spreadsheets', + 'https://www.googleapis.com/auth/drive.metadata', + ]; + + const now = moment().unix(); + + credentials.email = credentials.email.trim(); + const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); + + const signature = jwt.sign( + { + iss: credentials.email, + sub: credentials.delegatedEmail ?? credentials.email, + scope: scopes.join(' '), + aud: 'https://oauth2.googleapis.com/token', + iat: now, + exp: now + 3600, + }, + privateKey, + { + algorithm: 'RS256', + header: { + kid: privateKey, + typ: 'JWT', + alg: 'RS256', + }, + }, + ); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: signature, + }, + uri: 'https://oauth2.googleapis.com/token', + json: true, + }; + + return this.helpers.request(options); +} + export async function apiRequest( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -32,7 +89,7 @@ export async function apiRequest( method, body, qs, - uri: uri || `https://sheets.googleapis.com${resource}`, + uri: uri ?? `https://sheets.googleapis.com${resource}`, json: true, }; try { @@ -95,60 +152,3 @@ export async function apiRequestAllItems( return returnData; } - -export async function getAccessToken( - this: - | IExecuteFunctions - | IExecuteSingleFunctions - | ILoadOptionsFunctions - | ICredentialTestFunctions, - credentials: IGoogleAuthCredentials, -): Promise { - //https://developers.google.com/identity/protocols/oauth2/service-account#httprest - - const scopes = [ - 'https://www.googleapis.com/auth/drive.file', - 'https://www.googleapis.com/auth/spreadsheets', - 'https://www.googleapis.com/auth/drive.metadata', - ]; - - const now = moment().unix(); - - credentials.email = credentials.email.trim(); - const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); - - const signature = jwt.sign( - { - iss: credentials.email, - sub: credentials.delegatedEmail || credentials.email, - scope: scopes.join(' '), - aud: 'https://oauth2.googleapis.com/token', - iat: now, - exp: now + 3600, - }, - privateKey, - { - algorithm: 'RS256', - header: { - kid: privateKey, - typ: 'JWT', - alg: 'RS256', - }, - }, - ); - - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - form: { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: signature, - }, - uri: 'https://oauth2.googleapis.com/token', - json: true, - }; - - return this.helpers.request(options); -} diff --git a/packages/nodes-base/nodes/Google/Slides/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Slides/GenericFunctions.ts index e71a506d04..5474ad2322 100644 --- a/packages/nodes-base/nodes/Google/Slides/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Slides/GenericFunctions.ts @@ -15,6 +15,58 @@ interface IGoogleAuthCredentials { privateKey: string; } +async function getAccessToken( + this: IExecuteFunctions | ILoadOptionsFunctions, + credentials: IGoogleAuthCredentials, +) { + // https://developers.google.com/identity/protocols/oauth2/service-account#httprest + + const scopes = [ + 'https://www.googleapis.com/auth/drive.file', + 'https://www.googleapis.com/auth/presentations', + ]; + + const now = moment().unix(); + + credentials.email = credentials.email.trim(); + const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); + + const signature = jwt.sign( + { + iss: credentials.email, + sub: credentials.email, + scope: scopes.join(' '), + aud: 'https://oauth2.googleapis.com/token', + iat: now, + exp: now + 3600, + }, + privateKey, + { + algorithm: 'RS256', + header: { + kid: privateKey, + typ: 'JWT', + alg: 'RS256', + }, + }, + ); + + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + method: 'POST', + form: { + grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', + assertion: signature, + }, + uri: 'https://oauth2.googleapis.com/token', + json: true, + }; + + return this.helpers.request(options); +} + export async function googleApiRequest( this: IExecuteFunctions | ILoadOptionsFunctions, method: string, @@ -67,55 +119,3 @@ export async function googleApiRequest( throw new NodeApiError(this.getNode(), error); } } - -async function getAccessToken( - this: IExecuteFunctions | ILoadOptionsFunctions, - credentials: IGoogleAuthCredentials, -) { - // https://developers.google.com/identity/protocols/oauth2/service-account#httprest - - const scopes = [ - 'https://www.googleapis.com/auth/drive.file', - 'https://www.googleapis.com/auth/presentations', - ]; - - const now = moment().unix(); - - credentials.email = credentials.email.trim(); - const privateKey = credentials.privateKey.replace(/\\n/g, '\n').trim(); - - const signature = jwt.sign( - { - iss: credentials.email, - sub: credentials.email, - scope: scopes.join(' '), - aud: 'https://oauth2.googleapis.com/token', - iat: now, - exp: now + 3600, - }, - privateKey, - { - algorithm: 'RS256', - header: { - kid: privateKey, - typ: 'JWT', - alg: 'RS256', - }, - }, - ); - - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - method: 'POST', - form: { - grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', - assertion: signature, - }, - uri: 'https://oauth2.googleapis.com/token', - json: true, - }; - - return this.helpers.request(options); -} diff --git a/packages/nodes-base/nodes/Google/Slides/GoogleSlides.node.ts b/packages/nodes-base/nodes/Google/Slides/GoogleSlides.node.ts index 455c5b8451..2fd12a9804 100644 --- a/packages/nodes-base/nodes/Google/Slides/GoogleSlides.node.ts +++ b/packages/nodes-base/nodes/Google/Slides/GoogleSlides.node.ts @@ -542,7 +542,7 @@ export class GoogleSlides implements INodeType { return { replaceAllText: { replaceText: text.replaceText, - pageObjectIds: text.pageObjectIds || [], + pageObjectIds: text.pageObjectIds ?? [], containsText: { text: text.text, matchCase: text.matchCase, diff --git a/packages/nodes-base/nodes/Google/Task/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Task/GenericFunctions.ts index a08658f9fa..c4c10e0450 100644 --- a/packages/nodes-base/nodes/Google/Task/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Task/GenericFunctions.ts @@ -20,7 +20,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://www.googleapis.com${resource}`, + uri: uri ?? `https://www.googleapis.com${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Google/Translate/GenericFunctions.ts b/packages/nodes-base/nodes/Google/Translate/GenericFunctions.ts index 4df401976e..8667976d7b 100644 --- a/packages/nodes-base/nodes/Google/Translate/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/Translate/GenericFunctions.ts @@ -15,82 +15,6 @@ interface IGoogleAuthCredentials { privateKey: string; } -export async function googleApiRequest( - this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, - method: string, - resource: string, - - body: any = {}, - qs: IDataObject = {}, - uri?: string, - headers: IDataObject = {}, -): Promise { - const authenticationMethod = this.getNodeParameter( - 'authentication', - 0, - 'serviceAccount', - ) as string; - const options: OptionsWithUri = { - headers: { - 'Content-Type': 'application/json', - }, - method, - body, - qs, - uri: uri || `https://translation.googleapis.com${resource}`, - json: true, - }; - try { - if (Object.keys(headers).length !== 0) { - options.headers = Object.assign({}, options.headers, headers); - } - if (Object.keys(body).length === 0) { - delete options.body; - } - - if (authenticationMethod === 'serviceAccount') { - const credentials = await this.getCredentials('googleApi'); - - const { access_token } = await getAccessToken.call( - this, - credentials as unknown as IGoogleAuthCredentials, - ); - - options.headers!.Authorization = `Bearer ${access_token}`; - //@ts-ignore - return await this.helpers.request(options); - } else { - //@ts-ignore - return await this.helpers.requestOAuth2.call(this, 'googleTranslateOAuth2Api', options); - } - } catch (error) { - throw new NodeApiError(this.getNode(), error); - } -} - -export async function googleApiRequestAllItems( - this: IExecuteFunctions | ILoadOptionsFunctions, - propertyName: string, - method: string, - endpoint: string, - - body: any = {}, - query: IDataObject = {}, -): Promise { - const returnData: IDataObject[] = []; - - let responseData; - query.maxResults = 100; - - do { - responseData = await googleApiRequest.call(this, method, endpoint, body, query); - query.pageToken = responseData.nextPageToken; - returnData.push.apply(returnData, responseData[propertyName]); - } while (responseData.nextPageToken !== undefined && responseData.nextPageToken !== ''); - - return returnData; -} - async function getAccessToken( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, credentials: IGoogleAuthCredentials, @@ -142,3 +66,79 @@ async function getAccessToken( return this.helpers.request(options); } + +export async function googleApiRequest( + this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + method: string, + resource: string, + + body: any = {}, + qs: IDataObject = {}, + uri?: string, + headers: IDataObject = {}, +): Promise { + const authenticationMethod = this.getNodeParameter( + 'authentication', + 0, + 'serviceAccount', + ) as string; + const options: OptionsWithUri = { + headers: { + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri ?? `https://translation.googleapis.com${resource}`, + json: true, + }; + try { + if (Object.keys(headers).length !== 0) { + options.headers = Object.assign({}, options.headers, headers); + } + if (Object.keys(body).length === 0) { + delete options.body; + } + + if (authenticationMethod === 'serviceAccount') { + const credentials = await this.getCredentials('googleApi'); + + const { access_token } = await getAccessToken.call( + this, + credentials as unknown as IGoogleAuthCredentials, + ); + + options.headers!.Authorization = `Bearer ${access_token}`; + //@ts-ignore + return await this.helpers.request(options); + } else { + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, 'googleTranslateOAuth2Api', options); + } + } catch (error) { + throw new NodeApiError(this.getNode(), error); + } +} + +export async function googleApiRequestAllItems( + this: IExecuteFunctions | ILoadOptionsFunctions, + propertyName: string, + method: string, + endpoint: string, + + body: any = {}, + query: IDataObject = {}, +): Promise { + const returnData: IDataObject[] = []; + + let responseData; + query.maxResults = 100; + + do { + responseData = await googleApiRequest.call(this, method, endpoint, body, query); + query.pageToken = responseData.nextPageToken; + returnData.push.apply(returnData, responseData[propertyName]); + } while (responseData.nextPageToken !== undefined && responseData.nextPageToken !== ''); + + return returnData; +} diff --git a/packages/nodes-base/nodes/Google/YouTube/GenericFunctions.ts b/packages/nodes-base/nodes/Google/YouTube/GenericFunctions.ts index a7e1a31e37..0d7526f205 100644 --- a/packages/nodes-base/nodes/Google/YouTube/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Google/YouTube/GenericFunctions.ts @@ -21,7 +21,7 @@ export async function googleApiRequest( method, body, qs, - uri: uri || `https://www.googleapis.com${resource}`, + uri: uri ?? `https://www.googleapis.com${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Gotify/GenericFunctions.ts b/packages/nodes-base/nodes/Gotify/GenericFunctions.ts index 6e3c521144..6169dd161b 100644 --- a/packages/nodes-base/nodes/Gotify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Gotify/GenericFunctions.ts @@ -24,7 +24,7 @@ export async function gotifyApiRequest( }, body, qs, - uri: uri || `${credentials.url}${path}`, + uri: uri ?? `${credentials.url}${path}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Grafana/GenericFunctions.ts b/packages/nodes-base/nodes/Grafana/GenericFunctions.ts index 464d2dab9b..8238598b91 100644 --- a/packages/nodes-base/nodes/Grafana/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Grafana/GenericFunctions.ts @@ -6,6 +6,10 @@ import { OptionsWithUri } from 'request'; import { GrafanaCredentials } from './types'; +export function tolerateTrailingSlash(baseUrl: string) { + return baseUrl.endsWith('/') ? baseUrl.substr(0, baseUrl.length - 1) : baseUrl; +} + export async function grafanaApiRequest( this: IExecuteFunctions | ILoadOptionsFunctions, method: string, @@ -81,10 +85,6 @@ export function throwOnEmptyUpdate( } } -export function tolerateTrailingSlash(baseUrl: string) { - return baseUrl.endsWith('/') ? baseUrl.substr(0, baseUrl.length - 1) : baseUrl; -} - export function deriveUid(this: IExecuteFunctions, uidOrUrl: string) { if (!uidOrUrl.startsWith('http')) return uidOrUrl; diff --git a/packages/nodes-base/nodes/GraphQL/GraphQL.node.ts b/packages/nodes-base/nodes/GraphQL/GraphQL.node.ts index 7a45f54ea3..9f8cee069a 100644 --- a/packages/nodes-base/nodes/GraphQL/GraphQL.node.ts +++ b/packages/nodes-base/nodes/GraphQL/GraphQL.node.ts @@ -335,7 +335,7 @@ export class GraphQL implements INodeType { const responseFormat = this.getNodeParameter('responseFormat', 0) as string; const { parameter }: { parameter?: Array<{ name: string; value: string }> } = this.getNodeParameter('headerParametersUi', itemIndex, {}) as IDataObject; - const headerParameters = (parameter || []).reduce( + const headerParameters = (parameter ?? []).reduce( (result, item) => ({ ...result, [item.name]: item.value, @@ -398,9 +398,9 @@ export class GraphQL implements INodeType { throw new NodeOperationError( this.getNode(), 'Using variables failed:\n' + - requestOptions.body.variables + + (requestOptions.body.variables as string) + '\n\nWith error message:\n' + - error, + (error as string), { itemIndex }, ); } diff --git a/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts b/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts index a22c9877e7..77838c844c 100644 --- a/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Gumroad/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function gumroadApiRequest( method, qs, body, - uri: uri || `https://api.gumroad.com/v2${resource}`, + uri: uri ?? `https://api.gumroad.com/v2${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/HaloPSA/GenericFunctions.ts b/packages/nodes-base/nodes/HaloPSA/GenericFunctions.ts index 2124035ee1..aacb1abd07 100644 --- a/packages/nodes-base/nodes/HaloPSA/GenericFunctions.ts +++ b/packages/nodes-base/nodes/HaloPSA/GenericFunctions.ts @@ -23,6 +23,12 @@ interface IHaloPSATokens { id_token: string; } +function getAuthUrl(credentials: IDataObject) { + return credentials.hostingType === 'on-premise' + ? `${credentials.appUrl}/auth/token` + : `${credentials.authUrl}/token?tenant=${credentials.tenant}`; +} + // API Requests --------------------------------------------------------------------- export async function getAccessTokens( @@ -184,11 +190,6 @@ export async function haloPSAApiRequestAllItems( } // Utilities ------------------------------------------------------------------------ -function getAuthUrl(credentials: IDataObject) { - return credentials.hostingType === 'on-premise' - ? `${credentials.appUrl}/auth/token` - : `${credentials.authUrl}/token?tenant=${credentials.tenant}`; -} export function simplifyHaloPSAGetOutput( response: IDataObject[], diff --git a/packages/nodes-base/nodes/Harvest/GenericFunctions.ts b/packages/nodes-base/nodes/Harvest/GenericFunctions.ts index f9c149c9cd..f7b66b72dc 100644 --- a/packages/nodes-base/nodes/Harvest/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Harvest/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function harvestApiRequest( }, method, body, - uri: uri || `https://api.harvestapp.com/v2/${path}`, + uri: uri ?? `https://api.harvestapp.com/v2/${path}`, qs, json: true, }; diff --git a/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts b/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts index 1e777057ec..9713582036 100644 --- a/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts +++ b/packages/nodes-base/nodes/HelpScout/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function helpscoutApiRequest( method, body, qs, - uri: uri || `https://api.helpscout.net${resource}`, + uri: uri ?? `https://api.helpscout.net${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts b/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts index 881f9b8898..4b8017c57b 100644 --- a/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts +++ b/packages/nodes-base/nodes/HelpScout/HelpScout.node.ts @@ -504,7 +504,7 @@ export class HelpScout implements INodeType { ]; if (binaryProperty) { return { - fileName: binaryProperty.fileName || 'unknown', + fileName: binaryProperty.fileName ?? 'unknown', data: binaryProperty.data, mimeType: binaryProperty.mimeType, }; diff --git a/packages/nodes-base/nodes/HighLevel/GenericFunctions.ts b/packages/nodes-base/nodes/HighLevel/GenericFunctions.ts index c4c88b46be..b784ae2425 100644 --- a/packages/nodes-base/nodes/HighLevel/GenericFunctions.ts +++ b/packages/nodes-base/nodes/HighLevel/GenericFunctions.ts @@ -64,7 +64,7 @@ export async function dueDatePreSendAction( ); } const dueDate = dateToIsoSupressMillis(dueDateParam); - requestOptions.body = (requestOptions.body || {}) as object; + requestOptions.body = (requestOptions.body ?? {}) as object; Object.assign(requestOptions.body, { dueDate }); return requestOptions; } @@ -73,7 +73,7 @@ export async function contactIdentifierPreSendAction( this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions, ): Promise { - requestOptions.body = (requestOptions.body || {}) as object; + requestOptions.body = (requestOptions.body ?? {}) as object; let identifier = this.getNodeParameter('contactIdentifier', null) as string; if (!identifier) { const fields = this.getNodeParameter('updateFields') as { contactIdentifier: string }; @@ -93,7 +93,7 @@ export async function validEmailAndPhonePreSendAction( this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions, ): Promise { - const body = (requestOptions.body || {}) as { email?: string; phone?: string }; + const body = (requestOptions.body ?? {}) as { email?: string; phone?: string }; if (body.email && !isEmailValid(body.email)) { const message = `email "${body.email}" has invalid format`; @@ -112,7 +112,7 @@ export async function dateTimeToEpochPreSendAction( this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions, ): Promise { - const qs = (requestOptions.qs || {}) as { + const qs = (requestOptions.qs ?? {}) as { startDate?: string | number; endDate?: string | number; }; @@ -122,87 +122,6 @@ export async function dateTimeToEpochPreSendAction( return requestOptions; } -export async function opportunityUpdatePreSendAction( - this: IExecuteSingleFunctions, - requestOptions: IHttpRequestOptions, -): Promise { - const body = (requestOptions.body || {}) as { title?: string; status?: string }; - if (!body.status || !body.title) { - const pipelineId = this.getNodeParameter('pipelineId'); - const opportunityId = this.getNodeParameter('opportunityId'); - const resource = `/pipelines/${pipelineId}/opportunities/${opportunityId}`; - const responseData = await highLevelApiRequest.call(this, 'GET', resource); - body.status = body.status || responseData.status; - body.title = body.title || responseData.name; - requestOptions.body = body; - } - return requestOptions; -} - -export async function taskUpdatePreSendAction( - this: IExecuteSingleFunctions, - requestOptions: IHttpRequestOptions, -): Promise { - const body = (requestOptions.body || {}) as { title?: string; dueDate?: string }; - if (!body.title || !body.dueDate) { - const contactId = this.getNodeParameter('contactId'); - const taskId = this.getNodeParameter('taskId'); - const resource = `/contacts/${contactId}/tasks/${taskId}`; - const responseData = await highLevelApiRequest.call(this, 'GET', resource); - body.title = body.title || responseData.title; - // the api response dueDate has to be formatted or it will error on update - body.dueDate = body.dueDate || dateToIsoSupressMillis(responseData.dueDate); - requestOptions.body = body; - } - return requestOptions; -} - -export async function splitTagsPreSendAction( - this: IExecuteSingleFunctions, - requestOptions: IHttpRequestOptions, -): Promise { - const body = (requestOptions.body || {}) as IDataObject; - if (body.tags) { - if (Array.isArray(body.tags)) return requestOptions; - body.tags = (body.tags as string).split(',').map((tag) => tag.trim()); - } - return requestOptions; -} - -export async function highLevelApiPagination( - this: IExecutePaginationFunctions, - requestData: DeclarativeRestApiSettings.ResultOptions, -): Promise { - const responseData: INodeExecutionData[] = []; - const resource = this.getNodeParameter('resource') as string; - const returnAll = this.getNodeParameter('returnAll', false) as boolean; - - const resourceMapping: { [key: string]: string } = { - contact: 'contacts', - opportunity: 'opportunities', - }; - const rootProperty = resourceMapping[resource]; - - requestData.options.qs = requestData.options.qs || {}; - if (returnAll) requestData.options.qs.limit = 100; - - let responseTotal = 0; - - do { - const pageResponseData: INodeExecutionData[] = await this.makeRoutingRequest(requestData); - const items = pageResponseData[0].json[rootProperty] as []; - items.forEach((item) => responseData.push({ json: item })); - - const meta = pageResponseData[0].json.meta as IDataObject; - const startAfterId = meta.startAfterId as string; - const startAfter = meta.startAfter as number; - requestData.options.qs = { startAfterId, startAfter }; - responseTotal = (meta.total as number) || 0; - } while (returnAll && responseTotal > responseData.length); - - return responseData; -} - export async function highLevelApiRequest( this: | IExecuteFunctions @@ -222,7 +141,7 @@ export async function highLevelApiRequest( method, body, qs, - uri: uri || `https://rest.gohighlevel.com/v1${resource}`, + uri: uri ?? `https://rest.gohighlevel.com/v1${resource}`, json: true, }; if (!Object.keys(body).length) { @@ -235,6 +154,87 @@ export async function highLevelApiRequest( return this.helpers.requestWithAuthentication.call(this, 'highLevelApi', options); } +export async function opportunityUpdatePreSendAction( + this: IExecuteSingleFunctions, + requestOptions: IHttpRequestOptions, +): Promise { + const body = (requestOptions.body ?? {}) as { title?: string; status?: string }; + if (!body.status || !body.title) { + const pipelineId = this.getNodeParameter('pipelineId'); + const opportunityId = this.getNodeParameter('opportunityId'); + const resource = `/pipelines/${pipelineId}/opportunities/${opportunityId}`; + const responseData = await highLevelApiRequest.call(this, 'GET', resource); + body.status = body.status ?? responseData.status; + body.title = body.title ?? responseData.name; + requestOptions.body = body; + } + return requestOptions; +} + +export async function taskUpdatePreSendAction( + this: IExecuteSingleFunctions, + requestOptions: IHttpRequestOptions, +): Promise { + const body = (requestOptions.body ?? {}) as { title?: string; dueDate?: string }; + if (!body.title || !body.dueDate) { + const contactId = this.getNodeParameter('contactId'); + const taskId = this.getNodeParameter('taskId'); + const resource = `/contacts/${contactId}/tasks/${taskId}`; + const responseData = await highLevelApiRequest.call(this, 'GET', resource); + body.title = body.title ?? responseData.title; + // the api response dueDate has to be formatted or it will error on update + body.dueDate = body.dueDate ?? dateToIsoSupressMillis(responseData.dueDate); + requestOptions.body = body; + } + return requestOptions; +} + +export async function splitTagsPreSendAction( + this: IExecuteSingleFunctions, + requestOptions: IHttpRequestOptions, +): Promise { + const body = (requestOptions.body ?? {}) as IDataObject; + if (body.tags) { + if (Array.isArray(body.tags)) return requestOptions; + body.tags = (body.tags as string).split(',').map((tag) => tag.trim()); + } + return requestOptions; +} + +export async function highLevelApiPagination( + this: IExecutePaginationFunctions, + requestData: DeclarativeRestApiSettings.ResultOptions, +): Promise { + const responseData: INodeExecutionData[] = []; + const resource = this.getNodeParameter('resource') as string; + const returnAll = this.getNodeParameter('returnAll', false) as boolean; + + const resourceMapping: { [key: string]: string } = { + contact: 'contacts', + opportunity: 'opportunities', + }; + const rootProperty = resourceMapping[resource]; + + requestData.options.qs = requestData.options.qs ?? {}; + if (returnAll) requestData.options.qs.limit = 100; + + let responseTotal = 0; + + do { + const pageResponseData: INodeExecutionData[] = await this.makeRoutingRequest(requestData); + const items = pageResponseData[0].json[rootProperty] as []; + items.forEach((item) => responseData.push({ json: item })); + + const meta = pageResponseData[0].json.meta as IDataObject; + const startAfterId = meta.startAfterId as string; + const startAfter = meta.startAfter as number; + requestData.options.qs = { startAfterId, startAfter }; + responseTotal = (meta.total as number) || 0; + } while (returnAll && responseTotal > responseData.length); + + return responseData; +} + export async function getPipelineStages( this: ILoadOptionsFunctions, ): Promise { diff --git a/packages/nodes-base/nodes/HomeAssistant/GenericFunctions.ts b/packages/nodes-base/nodes/HomeAssistant/GenericFunctions.ts index e24a852446..f8b904ad48 100644 --- a/packages/nodes-base/nodes/HomeAssistant/GenericFunctions.ts +++ b/packages/nodes-base/nodes/HomeAssistant/GenericFunctions.ts @@ -79,7 +79,7 @@ export async function getHomeAssistantServices( for (const domainService of domainServices) { for (const [serviceID, value] of Object.entries(domainService.services)) { const serviceProperties = value as IDataObject; - const serviceName = serviceProperties.description || serviceID; + const serviceName = serviceProperties.description ?? serviceID; returnData.push({ name: serviceName as string, value: serviceID, diff --git a/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts b/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts index cf731ad498..b1f42e8a29 100644 --- a/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts +++ b/packages/nodes-base/nodes/HtmlExtract/HtmlExtract.node.ts @@ -24,7 +24,7 @@ const extractFunctions: { } = { attribute: ($: Cheerio, valueData: IValueData): string | undefined => $.attr(valueData.attribute!), - html: ($: Cheerio, _valueData: IValueData): string | undefined => $.html() || undefined, + html: ($: Cheerio, _valueData: IValueData): string | undefined => $.html() ?? undefined, text: ($: Cheerio, _valueData: IValueData): string | undefined => $.text(), value: ($: Cheerio, _valueData: IValueData): string | undefined => $.val(), }; diff --git a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts b/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts index 9f89dcb10c..1726d01816 100644 --- a/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Hubspot/GenericFunctions.ts @@ -36,7 +36,7 @@ export async function hubspotApiRequest( method, qs: query, headers: {}, - uri: uri || `https://api.hubapi.com${endpoint}`, + uri: uri ?? `https://api.hubapi.com${endpoint}`, body, json: true, useQuerystring: true, @@ -85,7 +85,7 @@ export async function hubspotApiRequestAllItems( let responseData; - query.limit = query.limit || 250; + query.limit = query.limit ?? 250; query.count = 100; body.limit = body.limit || 100; diff --git a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts index f2cfa45128..c13df80e35 100644 --- a/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts +++ b/packages/nodes-base/nodes/Hubspot/Hubspot.node.ts @@ -1412,8 +1412,8 @@ export class Hubspot implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i); const returnAll = this.getNodeParameter('returnAll', 0); const filtersGroupsUi = this.getNodeParameter('filterGroupsUi', i) as IDataObject; - const sortBy = additionalFields.sortBy || 'createdate'; - const direction = additionalFields.direction || 'DESCENDING'; + const sortBy = additionalFields.sortBy ?? 'createdate'; + const direction = additionalFields.direction ?? 'DESCENDING'; const body: IDataObject = { sorts: [ @@ -2232,8 +2232,8 @@ export class Hubspot implements INodeType { const additionalFields = this.getNodeParameter('additionalFields', i); const returnAll = this.getNodeParameter('returnAll', 0); const filtersGroupsUi = this.getNodeParameter('filterGroupsUi', i) as IDataObject; - const sortBy = additionalFields.sortBy || 'createdate'; - const direction = additionalFields.direction || 'DESCENDING'; + const sortBy = additionalFields.sortBy ?? 'createdate'; + const direction = additionalFields.direction ?? 'DESCENDING'; const body: IDataObject = { sorts: [ diff --git a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts index 11009302d1..f2620854a4 100644 --- a/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts +++ b/packages/nodes-base/nodes/Hubspot/HubspotTrigger.node.ts @@ -320,7 +320,7 @@ export class HubspotTrigger implements INodeType { let endpoint = `/webhooks/v3/${appId}/settings`; let body: IDataObject = { targetUrl: webhookUrl, - maxConcurrentRequests: additionalFields.maxConcurrentRequests || 5, + maxConcurrentRequests: additionalFields.maxConcurrentRequests ?? 5, }; await hubspotApiRequest.call(this, 'PUT', endpoint, body); diff --git a/packages/nodes-base/nodes/Hunter/GenericFunctions.ts b/packages/nodes-base/nodes/Hunter/GenericFunctions.ts index 224ee6ecb3..d418d623d4 100644 --- a/packages/nodes-base/nodes/Hunter/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Hunter/GenericFunctions.ts @@ -23,7 +23,7 @@ export async function hunterApiRequest( method, qs, body, - uri: uri || `https://api.hunter.io/v2${resource}`, + uri: uri ?? `https://api.hunter.io/v2${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/If/If.node.ts b/packages/nodes-base/nodes/If/If.node.ts index 03c0ee0c7a..ef62ad4ec8 100644 --- a/packages/nodes-base/nodes/If/If.node.ts +++ b/packages/nodes-base/nodes/If/If.node.ts @@ -324,13 +324,13 @@ export class If implements INodeType { [key: string]: (value1: NodeParameterValue, value2: NodeParameterValue) => boolean; } = { after: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) > (value2 || 0), + (value1 ?? 0) > (value2 ?? 0), before: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) < (value2 || 0), + (value1 ?? 0) < (value2 ?? 0), contains: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || '').toString().includes((value2 || '').toString()), + (value1 ?? '').toString().includes((value2 ?? '').toString()), notContains: (value1: NodeParameterValue, value2: NodeParameterValue) => - !(value1 || '').toString().includes((value2 || '').toString()), + !(value1 ?? '').toString().includes((value2 ?? '').toString()), endsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).endsWith(value2 as string), notEndsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => @@ -338,13 +338,13 @@ export class If implements INodeType { equal: (value1: NodeParameterValue, value2: NodeParameterValue) => value1 === value2, notEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => value1 !== value2, larger: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) > (value2 || 0), + (value1 ?? 0) > (value2 ?? 0), largerEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) >= (value2 || 0), + (value1 ?? 0) >= (value2 ?? 0), smaller: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) < (value2 || 0), + (value1 ?? 0) < (value2 ?? 0), smallerEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) <= (value2 || 0), + (value1 ?? 0) <= (value2 ?? 0), startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).startsWith(value2 as string), notStartsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => @@ -364,32 +364,32 @@ export class If implements INodeType { (isDateObject(value1) && isDateInvalid(value1)) ), regex: (value1: NodeParameterValue, value2: NodeParameterValue) => { - const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$')); + const regexMatch = (value2 ?? '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$')); let regex: RegExp; if (!regexMatch) { - regex = new RegExp((value2 || '').toString()); + regex = new RegExp((value2 ?? '').toString()); } else if (regexMatch.length === 1) { regex = new RegExp(regexMatch[1]); } else { regex = new RegExp(regexMatch[1], regexMatch[2]); } - return !!(value1 || '').toString().match(regex); + return !!(value1 ?? '').toString().match(regex); }, notRegex: (value1: NodeParameterValue, value2: NodeParameterValue) => { - const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$')); + const regexMatch = (value2 ?? '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$')); let regex: RegExp; if (!regexMatch) { - regex = new RegExp((value2 || '').toString()); + regex = new RegExp((value2 ?? '').toString()); } else if (regexMatch.length === 1) { regex = new RegExp(regexMatch[1]); } else { regex = new RegExp(regexMatch[1], regexMatch[2]); } - return !(value1 || '').toString().match(regex); + return !(value1 ?? '').toString().match(regex); }, }; diff --git a/packages/nodes-base/nodes/Intercom/GenericFunctions.ts b/packages/nodes-base/nodes/Intercom/GenericFunctions.ts index bfeaf71f46..d848c58bee 100644 --- a/packages/nodes-base/nodes/Intercom/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Intercom/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function intercomApiRequest( headers: headerWithAuthentication, method, qs: query, - uri: uri || `https://api.intercom.io${endpoint}`, + uri: uri ?? `https://api.intercom.io${endpoint}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts b/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts index 3f8efa2e13..277cbb6181 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/GenericFunctions.ts @@ -41,7 +41,7 @@ export async function invoiceNinjaApiRequest( const options: OptionsWithUri = { method, qs: query, - uri: uri || `${baseUrl}/api/v1${endpoint}`, + uri: uri ?? `${baseUrl}/api/v1${endpoint}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts index 7ecea8bace..5d3c83305a 100644 --- a/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts +++ b/packages/nodes-base/nodes/InvoiceNinja/InvoiceNinja.node.ts @@ -198,7 +198,7 @@ export class InvoiceNinja implements INodeType { '/invoices', ); for (const invoice of invoices) { - const invoiceName = (invoice.invoice_number || invoice.number) as string; + const invoiceName = (invoice.invoice_number ?? invoice.number) as string; const invoiceId = invoice.id as string; returnData.push({ name: invoiceName, diff --git a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts index 26681c80c6..f0cde87526 100644 --- a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts +++ b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts @@ -1,3 +1,4 @@ +import { NodeVM, NodeVMOptions } from 'vm2'; import { IExecuteFunctions } from 'n8n-core'; import { @@ -11,7 +12,42 @@ import { import { get, isEmpty, isEqual, isObject, lt, merge, pick, reduce, set, unset } from 'lodash'; -const { NodeVM } = require('vm2'); +const compareItems = ( + obj: INodeExecutionData, + obj2: INodeExecutionData, + keys: string[], + disableDotNotation: boolean, + _node: INode, +) => { + let result = true; + for (const key of keys) { + if (!disableDotNotation) { + if (!isEqual(get(obj.json, key), get(obj2.json, key))) { + result = false; + break; + } + } else { + if (!isEqual(obj.json[key], obj2.json[key])) { + result = false; + break; + } + } + } + return result; +}; + +const flattenKeys = (obj: IDataObject, path: string[] = []): IDataObject => { + return !isObject(obj) + ? { [path.join('.')]: obj } + : reduce(obj, (cum, next, key) => merge(cum, flattenKeys(next as IDataObject, [...path, key])), {}); //prettier-ignore +}; + +const shuffleArray = (array: any[]) => { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } +}; export class ItemLists implements INodeType { description: INodeTypeDescription = { @@ -1317,7 +1353,7 @@ return 0;`, console: mode === 'manual' ? 'redirect' : 'inherit', sandbox, }; - const vm = new NodeVM(options); + const vm = new NodeVM(options as unknown as NodeVMOptions); newItems = await vm.run( ` @@ -1360,40 +1396,3 @@ return 0;`, } } } - -const compareItems = ( - obj: INodeExecutionData, - obj2: INodeExecutionData, - keys: string[], - disableDotNotation: boolean, - _node: INode, -) => { - let result = true; - for (const key of keys) { - if (!disableDotNotation) { - if (!isEqual(get(obj.json, key), get(obj2.json, key))) { - result = false; - break; - } - } else { - if (!isEqual(obj.json[key], obj2.json[key])) { - result = false; - break; - } - } - } - return result; -}; - -const flattenKeys = (obj: IDataObject, path: string[] = []): IDataObject => { - return !isObject(obj) - ? { [path.join('.')]: obj } - : reduce(obj, (cum, next, key) => merge(cum, flattenKeys(next as IDataObject, [...path, key])), {}); //prettier-ignore -}; - -const shuffleArray = (array: any[]) => { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } -}; diff --git a/packages/nodes-base/nodes/Iterable/GenericFunctions.ts b/packages/nodes-base/nodes/Iterable/GenericFunctions.ts index ba327f37c6..2130d25691 100644 --- a/packages/nodes-base/nodes/Iterable/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Iterable/GenericFunctions.ts @@ -24,7 +24,7 @@ export async function iterableApiRequest( method, body, qs, - uri: uri || `https://api.iterable.com/api${resource}`, + uri: uri ?? `https://api.iterable.com/api${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Jenkins/GenericFunctions.ts b/packages/nodes-base/nodes/Jenkins/GenericFunctions.ts index 457fd4ba5d..580792e6d4 100644 --- a/packages/nodes-base/nodes/Jenkins/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Jenkins/GenericFunctions.ts @@ -9,6 +9,10 @@ import { import { IDataObject, NodeApiError } from 'n8n-workflow'; +export function tolerateTrailingSlash(baseUrl: string) { + return baseUrl.endsWith('/') ? baseUrl.substr(0, baseUrl.length - 1) : baseUrl; +} + export async function jenkinsApiRequest( this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -40,7 +44,3 @@ export async function jenkinsApiRequest( throw new NodeApiError(this.getNode(), error); } } - -export function tolerateTrailingSlash(baseUrl: string) { - return baseUrl.endsWith('/') ? baseUrl.substr(0, baseUrl.length - 1) : baseUrl; -} diff --git a/packages/nodes-base/nodes/Jira/GenericFunctions.ts b/packages/nodes-base/nodes/Jira/GenericFunctions.ts index 5df2d15155..d90bf6ae96 100644 --- a/packages/nodes-base/nodes/Jira/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Jira/GenericFunctions.ts @@ -40,7 +40,7 @@ export async function jiraSoftwareCloudApiRequest( }, method, qs: query, - uri: uri || `${domain}/rest${endpoint}`, + uri: uri ?? `${domain}/rest${endpoint}`, body, json: true, }; @@ -81,9 +81,12 @@ export async function jiraSoftwareCloudApiRequestAllItems( do { responseData = await jiraSoftwareCloudApiRequest.call(this, endpoint, method, body, query); returnData.push.apply(returnData, responseData[propertyName]); - query.startAt = responseData.startAt + responseData.maxResults; - body.startAt = responseData.startAt + responseData.maxResults; - } while (responseData.startAt + responseData.maxResults < responseData.total); + query.startAt = (responseData.startAt as number) + (responseData.maxResults as number); + body.startAt = (responseData.startAt as number) + (responseData.maxResults as number); + } while ( + (responseData.startAt as number) + (responseData.maxResults as number) < + responseData.total + ); return returnData; } diff --git a/packages/nodes-base/nodes/Jira/Jira.node.ts b/packages/nodes-base/nodes/Jira/Jira.node.ts index ace29863c9..85f09f4576 100644 --- a/packages/nodes-base/nodes/Jira/Jira.node.ts +++ b/packages/nodes-base/nodes/Jira/Jira.node.ts @@ -286,7 +286,7 @@ export class Jira implements INodeType { if (user.active) { activeUsers.push({ name: user.displayName as string, - value: (user.accountId || user.name) as string, + value: (user.accountId ?? user.name) as string, }); } return activeUsers; @@ -684,7 +684,7 @@ export class Jira implements INodeType { qs.expand = additionalFields.expand as string; } if (simplifyOutput) { - qs.expand = `${qs.expand || ''},names`; + qs.expand = `${qs.expand ?? ''},names`; } if (additionalFields.properties) { qs.properties = additionalFields.properties as string; @@ -702,7 +702,7 @@ export class Jira implements INodeType { if (simplifyOutput) { // Use rendered fields if requested and available - qs.expand = qs.expand || ''; + qs.expand = qs.expand ?? ''; if ( (qs.expand as string).toLowerCase().indexOf('renderedfields') !== -1 && responseData.renderedFields && diff --git a/packages/nodes-base/nodes/JotForm/GenericFunctions.ts b/packages/nodes-base/nodes/JotForm/GenericFunctions.ts index 04cfe5a9f8..823df81d5b 100644 --- a/packages/nodes-base/nodes/JotForm/GenericFunctions.ts +++ b/packages/nodes-base/nodes/JotForm/GenericFunctions.ts @@ -32,7 +32,7 @@ export async function jotformApiRequest( method, qs, form: body, - uri: uri || `https://${credentials.apiDomain || 'api.jotform.com'}${resource}`, + uri: uri ?? `https://${credentials.apiDomain || 'api.jotform.com'}${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/nodes/Kafka/KafkaTrigger.node.ts b/packages/nodes-base/nodes/Kafka/KafkaTrigger.node.ts index 676f1551c8..9a0fd50e7c 100644 --- a/packages/nodes-base/nodes/Kafka/KafkaTrigger.node.ts +++ b/packages/nodes-base/nodes/Kafka/KafkaTrigger.node.ts @@ -219,8 +219,6 @@ export class KafkaTrigger implements INodeType { await consumer.subscribe({ topic, fromBeginning: options.fromBeginning ? true : false }); - const self = this; - const useSchemaRegistry = this.getNodeParameter('useSchemaRegistry', 0) as boolean; const schemaRegistryUrl = this.getNodeParameter('schemaRegistryUrl', 0) as string; @@ -250,7 +248,7 @@ export class KafkaTrigger implements INodeType { const headers: { [key: string]: string } = {}; for (const key of Object.keys(message.headers)) { const header = message.headers[key]; - headers[key] = header?.toString('utf8') || ''; + headers[key] = header?.toString('utf8') ?? ''; } data.headers = headers; @@ -264,7 +262,7 @@ export class KafkaTrigger implements INodeType { data = value; } - self.emit([self.helpers.returnJsonArray([data])]); + this.emit([this.helpers.returnJsonArray([data])]); }, }); }; diff --git a/packages/nodes-base/nodes/Keap/GenericFunctions.ts b/packages/nodes-base/nodes/Keap/GenericFunctions.ts index d1142f3e78..7687941359 100644 --- a/packages/nodes-base/nodes/Keap/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Keap/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function keapApiRequest( method, body, qs, - uri: uri || `https://api.infusionsoft.com/crm/rest/v1${resource}`, + uri: uri ?? `https://api.infusionsoft.com/crm/rest/v1${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Kitemaker/GenericFunctions.ts b/packages/nodes-base/nodes/Kitemaker/GenericFunctions.ts index 37dd622146..2feb61364f 100644 --- a/packages/nodes-base/nodes/Kitemaker/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Kitemaker/GenericFunctions.ts @@ -29,6 +29,16 @@ export async function kitemakerRequest( return responseData; } +function getGroupAndItems(resource: 'space' | 'user' | 'workItem') { + const map: { [key: string]: { [key: string]: string } } = { + space: { group: 'organization', items: 'spaces' }, + user: { group: 'organization', items: 'users' }, + workItem: { group: 'workItems', items: 'workItems' }, + }; + + return [map[resource].group, map[resource].items]; +} + export async function kitemakerRequestAllItems( this: IExecuteFunctions, body: { query: string; variables: { [key: string]: string } }, @@ -55,16 +65,6 @@ export async function kitemakerRequestAllItems( return returnData; } -function getGroupAndItems(resource: 'space' | 'user' | 'workItem') { - const map: { [key: string]: { [key: string]: string } } = { - space: { group: 'organization', items: 'spaces' }, - user: { group: 'organization', items: 'users' }, - workItem: { group: 'workItems', items: 'workItems' }, - }; - - return [map[resource].group, map[resource].items]; -} - export function createLoadOptions( resources: Array<{ name?: string; username?: string; title?: string; id: string }>, ): Array<{ name: string; value: string }> { diff --git a/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts b/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts index 6a4783cd94..35fcb6b0fc 100644 --- a/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts +++ b/packages/nodes-base/nodes/KoBoToolbox/GenericFunctions.ts @@ -37,7 +37,7 @@ export async function koBoToolboxApiRequest( Object.assign(options, option); } if (options.url && !/^http(s)?:/.test(options.url)) { - options.url = credentials.URL + options.url; + options.url = (credentials.URL as string) + options.url; } let results = null; @@ -63,12 +63,11 @@ export async function koBoToolboxApiRequest( export async function koBoToolboxRawRequest( this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, option: IHttpRequestOptions, - // tslint:disable-next-line:no-any ): Promise { const credentials = await this.getCredentials('koBoToolboxApi'); if (option.url && !/^http(s)?:/.test(option.url)) { - option.url = credentials.URL + option.url; + option.url = (credentials.URL as string) + option.url; } return this.helpers.httpRequestWithAuthentication.call(this, 'koBoToolboxApi', option); @@ -159,7 +158,7 @@ export function formatSubmission( .split('/') .map((k) => _.trim(k, ' _')) .join('.'); - const leafKey = sanitizedKey.split('.').pop() || ''; + const leafKey = sanitizedKey.split('.').pop() ?? ''; let format = 'string'; if (_.some(numberMasks, (mask) => matchWildcard(leafKey, mask))) { format = 'number'; @@ -205,7 +204,7 @@ export async function downloadAttachments( const credentials = await this.getCredentials('koBoToolboxApi'); // Look for attachment links - there can be more than one - const attachmentList = (submission._attachments || submission.attachments) as any[]; // tslint:disable-line:no-any + const attachmentList = (submission._attachments ?? submission.attachments) as any[]; if (attachmentList?.length) { for (const [index, attachment] of attachmentList.entries()) { @@ -267,7 +266,7 @@ export async function downloadAttachments( if ('question' === options.binaryNamingScheme && relatedQuestion) { binaryName = relatedQuestion; } else { - binaryName = `${options.dataPropertyAttachmentsPrefixName || 'attachment_'}${index}`; + binaryName = `${options.dataPropertyAttachmentsPrefixName ?? 'attachment_'}${index}`; } binaryItem.binary![binaryName] = await this.helpers.prepareBinaryData( diff --git a/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts b/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts index 0ad35b3180..6156db7947 100644 --- a/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts +++ b/packages/nodes-base/nodes/KoBoToolbox/KoBoToolboxTrigger.node.ts @@ -79,7 +79,7 @@ export class KoBoToolboxTrigger implements INodeType { async checkExists(this: IHookFunctions): Promise { const webhookData = this.getWorkflowStaticData('node'); const webhookUrl = this.getNodeWebhookUrl('default'); - const formId = this.getNodeParameter('formId') as string; //tslint:disable-line:variable-name + const formId = this.getNodeParameter('formId') as string; const webhooks = await koBoToolboxApiRequest.call(this, { url: `/api/v2/assets/${formId}/hooks/`, }); @@ -96,7 +96,7 @@ export class KoBoToolboxTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); 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; const response = await koBoToolboxApiRequest.call(this, { method: 'POST', @@ -118,7 +118,7 @@ export class KoBoToolboxTrigger implements INodeType { async delete(this: IHookFunctions): Promise { const webhookData = this.getWorkflowStaticData('node'); - const formId = this.getNodeParameter('formId') as string; //tslint:disable-line:variable-name + const formId = this.getNodeParameter('formId') as string; try { await koBoToolboxApiRequest.call(this, { method: 'DELETE', diff --git a/packages/nodes-base/nodes/Line/GenericFunctions.ts b/packages/nodes-base/nodes/Line/GenericFunctions.ts index ebe60352b0..6c648e07de 100644 --- a/packages/nodes-base/nodes/Line/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Line/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function lineApiRequest( method, body, qs, - uri: uri || '', + uri: uri ?? '', json: true, }; options = Object.assign({}, options, option); @@ -36,7 +36,6 @@ export async function lineApiRequest( delete options.body; } - //@ts-ignore return await this.helpers.requestOAuth2.call(this, 'lineNotifyOAuth2Api', options, { tokenType: 'Bearer', }); diff --git a/packages/nodes-base/nodes/LingvaNex/GenericFunctions.ts b/packages/nodes-base/nodes/LingvaNex/GenericFunctions.ts index 43ccba7b8a..5e622d41bc 100644 --- a/packages/nodes-base/nodes/LingvaNex/GenericFunctions.ts +++ b/packages/nodes-base/nodes/LingvaNex/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function lingvaNexApiRequest( method, qs, body, - uri: uri || `https://api-b2b.backenster.com/b1/api/v3${resource}`, + uri: uri ?? `https://api-b2b.backenster.com/b1/api/v3${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/MQTT/MqttTrigger.node.ts b/packages/nodes-base/nodes/MQTT/MqttTrigger.node.ts index 4f7d746c95..619197c9f6 100644 --- a/packages/nodes-base/nodes/MQTT/MqttTrigger.node.ts +++ b/packages/nodes-base/nodes/MQTT/MqttTrigger.node.ts @@ -128,9 +128,7 @@ export class MqttTrigger implements INodeType { client = mqtt.connect(brokerUrl, clientOptions); } - const self = this; - - async function manualTriggerFunction() { + const manualTriggerFunction = async () => { await new Promise((resolve, reject) => { client.on('connect', () => { client.subscribe(topicsQoS as mqtt.ISubscriptionMap, (err, _granted) => { @@ -155,7 +153,7 @@ export class MqttTrigger implements INodeType { //@ts-ignore result = [message as string]; } - self.emit([self.helpers.returnJsonArray(result)]); + this.emit([this.helpers.returnJsonArray(result)]); resolve(true); }); }); @@ -165,7 +163,7 @@ export class MqttTrigger implements INodeType { reject(error); }); }); - } + }; if (this.getMode() === 'trigger') { await manualTriggerFunction(); diff --git a/packages/nodes-base/nodes/Magento/GenericFunctions.ts b/packages/nodes-base/nodes/Magento/GenericFunctions.ts index a7a53c7760..38626f4435 100644 --- a/packages/nodes-base/nodes/Magento/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Magento/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function magentoApiRequest( method, body, qs, - uri: uri || `${credentials.host}${resource}`, + uri: uri ?? `${credentials.host}${resource}`, json: true, }; @@ -205,6 +205,94 @@ export function adjustAddresses(addresses: [{ street: string; [key: string]: str return _addresses; } +function getConditionTypeFields(): INodeProperties { + return { + displayName: 'Condition Type', + name: 'condition_type', + type: 'options', + options: [ + { + name: 'Equals', + value: 'eq', + }, + { + name: 'Greater than', + value: 'gt', + }, + { + name: 'Greater than or equal', + value: 'gteq', + }, + { + name: 'In', + value: 'in', + description: 'The value can contain a comma-separated list of values', + }, + { + name: 'Less Than', + value: 'lt', + }, + { + name: 'Less Than or Equal', + value: 'lte', + }, + { + name: 'Like', + value: 'like', + description: 'The value can contain the SQL wildcard characters when like is specified', + }, + { + name: 'More or Equal', + value: 'moreq', + }, + { + name: 'Not Equal', + value: 'neq', + }, + { + name: 'Not In', + value: 'nin', + description: 'The value can contain a comma-separated list of values', + }, + { + name: 'Not Null', + value: 'notnull', + }, + { + name: 'Null', + value: 'null', + }, + ], + default: 'eq', + }; +} + +function getConditions(attributeFunction: string): INodeProperties[] { + return [ + { + displayName: 'Field', + name: 'field', + type: 'options', + typeOptions: { + loadOptionsMethod: attributeFunction, + }, + default: '', + }, + getConditionTypeFields(), + { + displayName: 'Value', + name: 'value', + type: 'string', + displayOptions: { + hide: { + condition_type: ['null', 'notnull'], + }, + }, + default: '', + }, + ]; +} + export function getSearchFilters( resource: string, filterableAttributeFunction: string, @@ -384,94 +472,6 @@ export function getSearchFilters( ]; } -function getConditionTypeFields(): INodeProperties { - return { - displayName: 'Condition Type', - name: 'condition_type', - type: 'options', - options: [ - { - name: 'Equals', - value: 'eq', - }, - { - name: 'Greater than', - value: 'gt', - }, - { - name: 'Greater than or equal', - value: 'gteq', - }, - { - name: 'In', - value: 'in', - description: 'The value can contain a comma-separated list of values', - }, - { - name: 'Less Than', - value: 'lt', - }, - { - name: 'Less Than or Equal', - value: 'lte', - }, - { - name: 'Like', - value: 'like', - description: 'The value can contain the SQL wildcard characters when like is specified', - }, - { - name: 'More or Equal', - value: 'moreq', - }, - { - name: 'Not Equal', - value: 'neq', - }, - { - name: 'Not In', - value: 'nin', - description: 'The value can contain a comma-separated list of values', - }, - { - name: 'Not Null', - value: 'notnull', - }, - { - name: 'Null', - value: 'null', - }, - ], - default: 'eq', - }; -} - -function getConditions(attributeFunction: string): INodeProperties[] { - return [ - { - displayName: 'Field', - name: 'field', - type: 'options', - typeOptions: { - loadOptionsMethod: attributeFunction, - }, - default: '', - }, - getConditionTypeFields(), - { - displayName: 'Value', - name: 'value', - type: 'string', - displayOptions: { - hide: { - condition_type: ['null', 'notnull'], - }, - }, - default: '', - }, - ]; -} - export function getFilterQuery(data: { conditions?: Filter[]; matchType: string; diff --git a/packages/nodes-base/nodes/Mailcheck/GenericFunctions.ts b/packages/nodes-base/nodes/Mailcheck/GenericFunctions.ts index cd795dc1e1..b7779cbc73 100644 --- a/packages/nodes-base/nodes/Mailcheck/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mailcheck/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function mailCheckApiRequest( method, body, qs, - uri: uri || `https://api.mailcheck.co/v1${resource}`, + uri: uri ?? `https://api.mailcheck.co/v1${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts b/packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts index 45dc33b457..5d6637241c 100644 --- a/packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mailchimp/GenericFunctions.ts @@ -9,6 +9,23 @@ import { import { IDataObject, NodeApiError, NodeOperationError } from 'n8n-workflow'; +async function getMetadata( + this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + oauthTokenData: IDataObject, +) { + const credentials = await this.getCredentials('mailchimpOAuth2Api'); + const options: OptionsWithUrl = { + headers: { + Accept: 'application/json', + Authorization: `OAuth ${oauthTokenData.access_token}`, + }, + method: 'GET', + url: credentials.metadataUrl as string, + json: true, + }; + return this.helpers.request(options); +} + export async function mailchimpApiRequest( this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, @@ -55,7 +72,6 @@ export async function mailchimpApiRequest( ); options.url = `${api_endpoint}/3.0${endpoint}`; - //@ts-ignore return await this.helpers.requestOAuth2.call(this, 'mailchimpOAuth2Api', options, { tokenType: 'Bearer', }); @@ -100,23 +116,6 @@ export function validateJSON(json: string | undefined): any { return result; } -async function getMetadata( - this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, - oauthTokenData: IDataObject, -) { - const credentials = await this.getCredentials('mailchimpOAuth2Api'); - const options: OptionsWithUrl = { - headers: { - Accept: 'application/json', - Authorization: `OAuth ${oauthTokenData.access_token}`, - }, - method: 'GET', - url: credentials.metadataUrl as string, - json: true, - }; - return this.helpers.request(options); -} - export const campaignFieldsMetadata = [ '*', 'campaigns.id', diff --git a/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts b/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts index cfc3cced20..973b0d33a8 100644 --- a/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts +++ b/packages/nodes-base/nodes/Mailgun/Mailgun.node.ts @@ -156,7 +156,7 @@ export class Mailgun implements INodeType { attachments.push({ value: binaryDataBuffer, options: { - filename: item.binary[propertyName].fileName || 'unknown', + filename: item.binary[propertyName].fileName ?? 'unknown', }, }); } diff --git a/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts b/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts index 2c0982eeaf..217164a5c3 100644 --- a/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mailjet/GenericFunctions.ts @@ -39,7 +39,7 @@ export async function mailjetApiRequest( method, qs, body, - uri: uri || `https://api.mailjet.com${path}`, + uri: uri ?? `https://api.mailjet.com${path}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Mandrill/Mandrill.node.ts b/packages/nodes-base/nodes/Mandrill/Mandrill.node.ts index 117e09916d..21c7bb5354 100644 --- a/packages/nodes-base/nodes/Mandrill/Mandrill.node.ts +++ b/packages/nodes-base/nodes/Mandrill/Mandrill.node.ts @@ -855,7 +855,7 @@ export class Mandrill implements INodeType { attachmentsBinary = _.map(attachmentsUi.attachmentsBinary, (o: IDataObject) => { if (items[i].binary!.hasOwnProperty(o.property as string)) { const aux: IDataObject = {}; - aux.name = items[i].binary![o.property as string].fileName || 'unknown'; + aux.name = items[i].binary![o.property as string].fileName ?? 'unknown'; aux.content = items[i].binary![o.property as string].data; aux.type = items[i].binary![o.property as string].mimeType; return aux; diff --git a/packages/nodes-base/nodes/Matrix/GenericFunctions.ts b/packages/nodes-base/nodes/Matrix/GenericFunctions.ts index 261b539ec7..2d1dc762af 100644 --- a/packages/nodes-base/nodes/Matrix/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Matrix/GenericFunctions.ts @@ -18,7 +18,7 @@ export async function matrixApiRequest( ) { let options: OptionsWithUri = { method, - headers: headers || { + headers: headers ?? { 'Content-Type': 'application/json; charset=utf-8', }, body, @@ -38,7 +38,7 @@ export async function matrixApiRequest( options.uri = `${credentials.homeserverUrl}/_matrix/${ //@ts-ignore - option.overridePrefix || 'client' + option.overridePrefix ?? 'client' }/r0${resource}`; options.headers!.Authorization = `Bearer ${credentials.accessToken}`; const response = await this.helpers.request(options); diff --git a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts index 62baf83dc0..b620c59bed 100644 --- a/packages/nodes-base/nodes/Mautic/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Mautic/GenericFunctions.ts @@ -23,7 +23,7 @@ export async function mauticApiRequest( headers: {}, method, qs: query, - uri: uri || `/api${endpoint}`, + uri: uri ?? `/api${endpoint}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/Medium/GenericFunctions.ts b/packages/nodes-base/nodes/Medium/GenericFunctions.ts index 9c3a1d9616..ea10e1050c 100644 --- a/packages/nodes-base/nodes/Medium/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Medium/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function mediumApiRequest( 'Accept-Charset': 'utf-8', }, qs: query, - uri: uri || `https://api.medium.com/v1${endpoint}`, + uri: uri ?? `https://api.medium.com/v1${endpoint}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/Merge/v2/GenericFunctions.ts b/packages/nodes-base/nodes/Merge/v2/GenericFunctions.ts index 37390aed36..1d85860037 100644 --- a/packages/nodes-base/nodes/Merge/v2/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Merge/v2/GenericFunctions.ts @@ -122,6 +122,93 @@ function findFirstMatch( return [{ entry: data[index], index }]; } +const parseStringAndCompareToObject = (str: string, arr: IDataObject) => { + try { + const parsedArray = jsonParse(str); + return isEqual(parsedArray, arr); + } catch (error) { + return false; + } +}; + +function isFalsy(value: T) { + if (isNull(value)) return true; + if (typeof value === 'string' && value === '') return true; + if (Array.isArray(value) && value.length === 0) return true; + return false; +} + +const fuzzyCompare = + (options: IDataObject) => + (item1: T, item2: U) => { + //Fuzzy compare is disabled, so we do strict comparison + if (!options.fuzzyCompare) return isEqual(item1, item2); + + //Both types are the same, so we do strict comparison + if (!isNull(item1) && !isNull(item2) && typeof item1 === typeof item2) { + return isEqual(item1, item2); + } + + //Null, empty strings, empty arrays all treated as the same + if (isFalsy(item1) && isFalsy(item2)) return true; + + //When a field is missing in one branch and isFalsy() in another, treat them as matching + if (isFalsy(item1) && item2 === undefined) return true; + if (item1 === undefined && isFalsy(item2)) return true; + + //Compare numbers and strings representing that number + if (typeof item1 === 'number' && typeof item2 === 'string') { + return item1.toString() === item2; + } + + if (typeof item1 === 'string' && typeof item2 === 'number') { + return item1 === item2.toString(); + } + + //Compare objects/arrays and their stringified version + if (!isNull(item1) && typeof item1 === 'object' && typeof item2 === 'string') { + return parseStringAndCompareToObject(item2, item1 as IDataObject); + } + + if (!isNull(item2) && typeof item1 === 'string' && typeof item2 === 'object') { + return parseStringAndCompareToObject(item1, item2 as IDataObject); + } + + //Compare booleans and strings representing the boolean (’true’, ‘True’, ‘TRUE’) + if (typeof item1 === 'boolean' && typeof item2 === 'string') { + if (item1 === true && item2.toLocaleLowerCase() === 'true') return true; + if (item1 === false && item2.toLocaleLowerCase() === 'false') return true; + } + + if (typeof item2 === 'boolean' && typeof item1 === 'string') { + if (item2 === true && item1.toLocaleLowerCase() === 'true') return true; + if (item2 === false && item1.toLocaleLowerCase() === 'false') return true; + } + + //Compare booleans and the numbers/string 0 and 1 + if (typeof item1 === 'boolean' && typeof item2 === 'number') { + if (item1 === true && item2 === 1) return true; + if (item1 === false && item2 === 0) return true; + } + + if (typeof item2 === 'boolean' && typeof item1 === 'number') { + if (item2 === true && item1 === 1) return true; + if (item2 === false && item1 === 0) return true; + } + + if (typeof item1 === 'boolean' && typeof item2 === 'string') { + if (item1 === true && item2 === '1') return true; + if (item1 === false && item2 === '0') return true; + } + + if (typeof item2 === 'boolean' && typeof item1 === 'string') { + if (item2 === true && item1 === '1') return true; + if (item2 === false && item1 === '0') return true; + } + + return isEqual(item1, item2); + }; + export function findMatches( input1: INodeExecutionData[], input2: INodeExecutionData[], @@ -210,6 +297,38 @@ export function findMatches( return filteredData; } +export function selectMergeMethod(clashResolveOptions: ClashResolveOptions) { + const mergeMode = clashResolveOptions.mergeMode as string; + + if (clashResolveOptions.overrideEmpty) { + function customizer(targetValue: GenericValue, srcValue: GenericValue) { + if (srcValue === undefined || srcValue === null || srcValue === '') { + return targetValue; + } + } + if (mergeMode === 'deepMerge') { + return (target: IDataObject, ...source: IDataObject[]) => { + const targetCopy = Object.assign({}, target); + return mergeWith(targetCopy, ...source, customizer); + }; + } + if (mergeMode === 'shallowMerge') { + return (target: IDataObject, ...source: IDataObject[]) => { + const targetCopy = Object.assign({}, target); + return assignWith(targetCopy, ...source, customizer); + }; + } + } else { + if (mergeMode === 'deepMerge') { + return (target: IDataObject, ...source: IDataObject[]) => merge({}, target, ...source); + } + if (mergeMode === 'shallowMerge') { + return (target: IDataObject, ...source: IDataObject[]) => assign({}, target, ...source); + } + } + return (target: IDataObject, ...source: IDataObject[]) => merge({}, target, ...source); +} + export function mergeMatched( matched: EntryMatches[], clashResolveOptions: ClashResolveOptions, @@ -292,38 +411,6 @@ export function mergeMatched( return returnData; } -export function selectMergeMethod(clashResolveOptions: ClashResolveOptions) { - const mergeMode = clashResolveOptions.mergeMode as string; - - if (clashResolveOptions.overrideEmpty) { - function customizer(targetValue: GenericValue, srcValue: GenericValue) { - if (srcValue === undefined || srcValue === null || srcValue === '') { - return targetValue; - } - } - if (mergeMode === 'deepMerge') { - return (target: IDataObject, ...source: IDataObject[]) => { - const targetCopy = Object.assign({}, target); - return mergeWith(targetCopy, ...source, customizer); - }; - } - if (mergeMode === 'shallowMerge') { - return (target: IDataObject, ...source: IDataObject[]) => { - const targetCopy = Object.assign({}, target); - return assignWith(targetCopy, ...source, customizer); - }; - } - } else { - if (mergeMode === 'deepMerge') { - return (target: IDataObject, ...source: IDataObject[]) => merge({}, target, ...source); - } - if (mergeMode === 'shallowMerge') { - return (target: IDataObject, ...source: IDataObject[]) => assign({}, target, ...source); - } - } - return (target: IDataObject, ...source: IDataObject[]) => merge({}, target, ...source); -} - export function checkMatchFieldsInput(data: IDataObject[]) { if (data.length === 1 && data[0].field1 === '' && data[0].field2 === '') { throw new Error( @@ -374,90 +461,3 @@ export function addSourceField(data: INodeExecutionData[], sourceField: string) }; }); } - -const fuzzyCompare = - (options: IDataObject) => - (item1: T, item2: U) => { - //Fuzzy compare is disabled, so we do strict comparison - if (!options.fuzzyCompare) return isEqual(item1, item2); - - //Both types are the same, so we do strict comparison - if (!isNull(item1) && !isNull(item2) && typeof item1 === typeof item2) { - return isEqual(item1, item2); - } - - //Null, empty strings, empty arrays all treated as the same - if (isFalsy(item1) && isFalsy(item2)) return true; - - //When a field is missing in one branch and isFalsy() in another, treat them as matching - if (isFalsy(item1) && item2 === undefined) return true; - if (item1 === undefined && isFalsy(item2)) return true; - - //Compare numbers and strings representing that number - if (typeof item1 === 'number' && typeof item2 === 'string') { - return item1.toString() === item2; - } - - if (typeof item1 === 'string' && typeof item2 === 'number') { - return item1 === item2.toString(); - } - - //Compare objects/arrays and their stringified version - if (!isNull(item1) && typeof item1 === 'object' && typeof item2 === 'string') { - return parseStringAndCompareToObject(item2, item1 as IDataObject); - } - - if (!isNull(item2) && typeof item1 === 'string' && typeof item2 === 'object') { - return parseStringAndCompareToObject(item1, item2 as IDataObject); - } - - //Compare booleans and strings representing the boolean (’true’, ‘True’, ‘TRUE’) - if (typeof item1 === 'boolean' && typeof item2 === 'string') { - if (item1 === true && item2.toLocaleLowerCase() === 'true') return true; - if (item1 === false && item2.toLocaleLowerCase() === 'false') return true; - } - - if (typeof item2 === 'boolean' && typeof item1 === 'string') { - if (item2 === true && item1.toLocaleLowerCase() === 'true') return true; - if (item2 === false && item1.toLocaleLowerCase() === 'false') return true; - } - - //Compare booleans and the numbers/string 0 and 1 - if (typeof item1 === 'boolean' && typeof item2 === 'number') { - if (item1 === true && item2 === 1) return true; - if (item1 === false && item2 === 0) return true; - } - - if (typeof item2 === 'boolean' && typeof item1 === 'number') { - if (item2 === true && item1 === 1) return true; - if (item2 === false && item1 === 0) return true; - } - - if (typeof item1 === 'boolean' && typeof item2 === 'string') { - if (item1 === true && item2 === '1') return true; - if (item1 === false && item2 === '0') return true; - } - - if (typeof item2 === 'boolean' && typeof item1 === 'string') { - if (item2 === true && item1 === '1') return true; - if (item2 === false && item1 === '0') return true; - } - - return isEqual(item1, item2); - }; - -const parseStringAndCompareToObject = (str: string, arr: IDataObject) => { - try { - const parsedArray = jsonParse(str); - return isEqual(parsedArray, arr); - } catch (error) { - return false; - } -}; - -function isFalsy(value: T) { - if (isNull(value)) return true; - if (typeof value === 'string' && value === '') return true; - if (Array.isArray(value) && value.length === 0) return true; - return false; -} diff --git a/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts index badd717691..1e1d248f1a 100644 --- a/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/Dynamics/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function microsoftApiRequest( method, body, qs, - uri: uri || `https://${credentials.subdomain}.${credentials.region}/api/data/v9.2${resource}`, + uri: uri ?? `https://${credentials.subdomain}.${credentials.region}/api/data/v9.2${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Microsoft/Excel/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Excel/GenericFunctions.ts index aacb40c23d..26aff346aa 100644 --- a/packages/nodes-base/nodes/Microsoft/Excel/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/Excel/GenericFunctions.ts @@ -19,7 +19,7 @@ export async function microsoftApiRequest( method, body, qs, - uri: uri || `https://graph.microsoft.com/v1.0/me${resource}`, + uri: uri ?? `https://graph.microsoft.com/v1.0/me${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Microsoft/OneDrive/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/OneDrive/GenericFunctions.ts index d4d6509469..eb85f887be 100644 --- a/packages/nodes-base/nodes/Microsoft/OneDrive/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/OneDrive/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function microsoftApiRequest( method, body, qs, - uri: uri || `https://graph.microsoft.com/v1.0/me${resource}`, + uri: uri ?? `https://graph.microsoft.com/v1.0/me${resource}`, }; try { Object.assign(options, option); diff --git a/packages/nodes-base/nodes/Microsoft/Outlook/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Outlook/GenericFunctions.ts index 71285ed852..7381a21641 100644 --- a/packages/nodes-base/nodes/Microsoft/Outlook/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/Outlook/GenericFunctions.ts @@ -35,7 +35,7 @@ export async function microsoftApiRequest( method, body, qs, - uri: uri || apiUrl, + uri: uri ?? apiUrl, }; try { Object.assign(options, option); diff --git a/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts index 7c53d59bcb..b734b52087 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/GenericFunctions.ts @@ -21,7 +21,7 @@ export async function microsoftApiRequest( method, body, qs, - uri: uri || `https://graph.microsoft.com${resource}`, + uri: uri ?? `https://graph.microsoft.com${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts index c0e9caf91f..2ddeac8725 100644 --- a/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts +++ b/packages/nodes-base/nodes/Microsoft/Teams/MicrosoftTeams.node.ts @@ -245,7 +245,9 @@ export class MicrosoftTeams implements INodeType { .map((member: IDataObject) => member.displayName) .join(', '); } - const chatName = `${chat.topic || '(no title) - ' + chat.id} (${chat.chatType})`; + const chatName = `${chat.topic || '(no title) - ' + (chat.id as string)} (${ + chat.chatType + })`; const chatId = chat.id; returnData.push({ name: chatName, diff --git a/packages/nodes-base/nodes/Microsoft/ToDo/GenericFunctions.ts b/packages/nodes-base/nodes/Microsoft/ToDo/GenericFunctions.ts index a8e1dbdf00..6bb66ad2ae 100644 --- a/packages/nodes-base/nodes/Microsoft/ToDo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Microsoft/ToDo/GenericFunctions.ts @@ -21,7 +21,7 @@ export async function microsoftApiRequest( method, body, qs, - uri: uri || `https://graph.microsoft.com/v1.0/me${resource}`, + uri: uri ?? `https://graph.microsoft.com/v1.0/me${resource}`, }; try { Object.assign(options, option); diff --git a/packages/nodes-base/nodes/MySql/GenericFunctions.ts b/packages/nodes-base/nodes/MySql/GenericFunctions.ts index bf9b4aa634..376d0cfb22 100644 --- a/packages/nodes-base/nodes/MySql/GenericFunctions.ts +++ b/packages/nodes-base/nodes/MySql/GenericFunctions.ts @@ -62,7 +62,7 @@ export async function searchTables( const sql = ` SELECT table_name FROM information_schema.tables WHERE table_schema = '${credentials.database}' - and table_name like '%${query || ''}%' + and table_name like '%${query ?? ''}%' ORDER BY table_name `; const [rows] = await connection.query(sql); diff --git a/packages/nodes-base/nodes/N8n/GenericFunctions.ts b/packages/nodes-base/nodes/N8n/GenericFunctions.ts index b069ef0e15..1712d0e693 100644 --- a/packages/nodes-base/nodes/N8n/GenericFunctions.ts +++ b/packages/nodes-base/nodes/N8n/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function apiRequest( body: object, query?: IDataObject, ): Promise { - query = query || {}; + query = query ?? {}; type N8nApiCredentials = { apiKey: string; @@ -60,7 +60,7 @@ export async function apiRequestAllItems( body: object, query?: IDataObject, ): Promise { - query = query || {}; + query = query ?? {}; const returnData: IDataObject[] = []; let nextCursor: string | undefined = undefined; @@ -190,11 +190,11 @@ export const prepareWorkflowCreateBody: PreSendAction = async function ( const body = requestOptions.body as IDataObject; const newBody: IDataObject = {}; - newBody.name = body.name || 'My workflow'; - newBody.nodes = body.nodes || []; - newBody.settings = body.settings || {}; - newBody.connections = body.connections || {}; - newBody.staticData = body.staticData || null; + newBody.name = body.name ?? 'My workflow'; + newBody.nodes = body.nodes ?? []; + newBody.settings = body.settings ?? {}; + newBody.connections = body.connections ?? {}; + newBody.staticData = body.staticData ?? null; requestOptions.body = newBody; diff --git a/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts b/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts index 1685f06b05..95b5f6798c 100644 --- a/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts +++ b/packages/nodes-base/nodes/N8nTrigger/N8nTrigger.node.ts @@ -55,18 +55,17 @@ export class N8nTrigger implements INodeType { ]); } - const self = this; - async function manualTriggerFunction() { - self.emit([ - self.helpers.returnJsonArray([ + const manualTriggerFunction = async () => { + this.emit([ + this.helpers.returnJsonArray([ { event: 'Manual execution', timestamp: new Date().toISOString(), - workflow_id: self.getWorkflow().id, + workflow_id: this.getWorkflow().id, }, ]), ]); - } + }; return { manualTriggerFunction, diff --git a/packages/nodes-base/nodes/Nasa/GenericFunctions.ts b/packages/nodes-base/nodes/Nasa/GenericFunctions.ts index eee6487f71..2b168b133c 100644 --- a/packages/nodes-base/nodes/Nasa/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Nasa/GenericFunctions.ts @@ -19,7 +19,7 @@ export async function nasaApiRequest( const options: OptionsWithUri = { method, qs, - uri: uri || `https://api.nasa.gov${endpoint}`, + uri: uri ?? `https://api.nasa.gov${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/Netlify/GenericFunctions.ts b/packages/nodes-base/nodes/Netlify/GenericFunctions.ts index e1d9a3081a..2bbc82035c 100644 --- a/packages/nodes-base/nodes/Netlify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Netlify/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function netlifyApiRequest( }, qs: query, body, - uri: uri || `https://api.netlify.com/api/v1${endpoint}`, + uri: uri ?? `https://api.netlify.com/api/v1${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts b/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts index 30656a7f86..b164d62043 100644 --- a/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts +++ b/packages/nodes-base/nodes/NocoDB/GenericFunctions.ts @@ -40,7 +40,7 @@ export async function apiRequest( const baseUrl = credentials.host as string; - query = query || {}; + query = query ?? {}; const options: OptionsWithUri = { method, diff --git a/packages/nodes-base/nodes/Notion/GenericFunctions.ts b/packages/nodes-base/nodes/Notion/GenericFunctions.ts index 3762f1c4e0..53c00cf725 100644 --- a/packages/nodes-base/nodes/Notion/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Notion/GenericFunctions.ts @@ -53,7 +53,7 @@ export async function notionApiRequest( method, qs, body, - uri: uri || `https://api.notion.com/v1${resource}`, + uri: uri ?? `https://api.notion.com/v1${resource}`, json: true, }; options = Object.assign({}, options, option); @@ -254,6 +254,13 @@ export function formatBlocks(blocks: IDataObject[]) { return results; } +function getDateFormat(includeTime: boolean) { + if (!includeTime) { + return 'yyyy-MM-DD'; + } + return ''; +} + function getPropertyKeyValue(value: any, type: string, timezone: string, version = 1) { const ignoreIfEmpty = (v: T, cb: (v: T) => any) => !v && value.ignoreIfEmpty ? undefined : cb(v); @@ -296,7 +303,6 @@ function getPropertyKeyValue(value: any, type: string, timezone: string, version ? multiSelectValue : multiSelectValue.split(',').map((v: string) => v.trim()) ) - // tslint:disable-next-line: no-any .filter((entry: any) => entry !== null) .map((option: string) => (!uuidValidate(option) ? { name: option } : { id: option })), }; @@ -378,13 +384,6 @@ function getPropertyKeyValue(value: any, type: string, timezone: string, version return result; } -function getDateFormat(includeTime: boolean) { - if (!includeTime) { - return 'yyyy-MM-DD'; - } - return ''; -} - function getNameAndType(key: string) { const [name, type] = key.split('|'); return { @@ -428,7 +427,6 @@ export function mapSorting( } export function mapFilters(filtersList: IDataObject[], timezone: string) { - // tslint:disable-next-line: no-any return filtersList.reduce((obj, value: { [key: string]: any }) => { let key = getNameAndType(value.key).type; @@ -507,13 +505,13 @@ function simplifyProperty(property: any) { } } else if (['multi_select'].includes(property.type)) { if (Array.isArray(property[type])) { - result = property[type].map((e: IDataObject) => e.name || {}); + result = property[type].map((e: IDataObject) => e.name ?? {}); } else { - result = property[type].options.map((e: IDataObject) => e.name || {}); + result = property[type].options.map((e: IDataObject) => e.name ?? {}); } } else if (['relation'].includes(property.type)) { if (Array.isArray(property[type])) { - result = property[type].map((e: IDataObject) => e.id || {}); + result = property[type].map((e: IDataObject) => e.id ?? {}); } else { result = property[type].database_id; } @@ -548,6 +546,21 @@ export function simplifyProperties(properties: any) { return results; } +export function getPropertyTitle(properties: { [key: string]: any }) { + return ( + Object.values(properties).filter((property) => property.type === 'title')[0].title[0] + ?.plain_text || '' + ); +} + +function prepend(stringKey: string, properties: { [key: string]: any }) { + for (const key of Object.keys(properties)) { + properties[`${stringKey}_${snakeCase(key)}`] = properties[key]; + delete properties[key]; + } + return properties; +} + export function simplifyObjects(objects: any, download = false, version = 2) { if (!Array.isArray(objects)) { objects = [objects]; @@ -734,7 +747,7 @@ export function getConditions() { } // prettier-ignore -export async function downloadFiles(this: IExecuteFunctions | IPollFunctions, records: [{ properties: { [key: string]: any | { id: string; type: string; files: [{ external: { url: string } } | { file: { url: string } }] } } }]): Promise { // tslint:disable-line:no-any +export async function downloadFiles(this: IExecuteFunctions | IPollFunctions, records: [{ properties: { [key: string]: any | { id: string; type: string; files: [{ external: { url: string } } | { file: { url: string } }] } } }]): Promise { const elements: INodeExecutionData[] = []; for (const record of records) { @@ -788,21 +801,6 @@ export function extractDatabaseId(database: string) { } } -function prepend(stringKey: string, properties: { [key: string]: any }) { - for (const key of Object.keys(properties)) { - properties[`${stringKey}_${snakeCase(key)}`] = properties[key]; - delete properties[key]; - } - return properties; -} - -export function getPropertyTitle(properties: { [key: string]: any }) { - return ( - Object.values(properties).filter((property) => property.type === 'title')[0].title[0] - ?.plain_text || '' - ); -} - export function getSearchFilters(resource: string) { return [ { diff --git a/packages/nodes-base/nodes/Odoo/GenericFunctions.ts b/packages/nodes-base/nodes/Odoo/GenericFunctions.ts index 0d649d29dc..4153502a42 100644 --- a/packages/nodes-base/nodes/Odoo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Odoo/GenericFunctions.ts @@ -236,7 +236,7 @@ export async function odooGet( mapOdooResources[resource] || resource, mapOperationToJSONRPC[operation], [+itemsID] || [], - fieldsToReturn || [], + fieldsToReturn ?? [], ], }, id: Math.floor(Math.random() * 100), @@ -275,7 +275,7 @@ export async function odooGetAll( mapOdooResources[resource] || resource, mapOperationToJSONRPC[operation], (filters && processFilters(filters)) || [], - fieldsToReturn || [], + fieldsToReturn ?? [], 0, // offset limit, ], diff --git a/packages/nodes-base/nodes/OneSimpleApi/GenericFunctions.ts b/packages/nodes-base/nodes/OneSimpleApi/GenericFunctions.ts index 42bf45f14e..479b1a58d2 100644 --- a/packages/nodes-base/nodes/OneSimpleApi/GenericFunctions.ts +++ b/packages/nodes-base/nodes/OneSimpleApi/GenericFunctions.ts @@ -21,7 +21,7 @@ export async function oneSimpleApiRequest( body, qs, uri: - uri || + uri ?? `https://onesimpleapi.com/api${resource}?token=${credentials.apiToken}&output=${outputFormat}`, json: true, }; diff --git a/packages/nodes-base/nodes/Onfleet/GenericFunctions.ts b/packages/nodes-base/nodes/Onfleet/GenericFunctions.ts index f276b5b5e0..4b018e842b 100644 --- a/packages/nodes-base/nodes/Onfleet/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Onfleet/GenericFunctions.ts @@ -37,7 +37,7 @@ export async function onfleetApiRequest( method, body, qs, - uri: uri || `https://onfleet.com/api/v2/${resource}`, + uri: uri ?? `https://onfleet.com/api/v2/${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/OpenThesaurus/GenericFunctions.ts b/packages/nodes-base/nodes/OpenThesaurus/GenericFunctions.ts index 7b4c69c193..87fac2c57c 100644 --- a/packages/nodes-base/nodes/OpenThesaurus/GenericFunctions.ts +++ b/packages/nodes-base/nodes/OpenThesaurus/GenericFunctions.ts @@ -27,7 +27,7 @@ export async function openThesaurusApiRequest( method, qs, body, - uri: uri || `https://www.openthesaurus.de${resource}`, + uri: uri ?? `https://www.openthesaurus.de${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Orbit/GenericFunctions.ts b/packages/nodes-base/nodes/Orbit/GenericFunctions.ts index f6022a6c59..fc102df60f 100644 --- a/packages/nodes-base/nodes/Orbit/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Orbit/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function orbitApiRequest( method, qs, body, - uri: uri || `https://app.orbit.love/api/v1${resource}`, + uri: uri ?? `https://app.orbit.love/api/v1${resource}`, json: true, }; @@ -42,6 +42,43 @@ export async function orbitApiRequest( } } +export function resolveIdentities(responseData: IRelation) { + const identities: IDataObject = {}; + for (const data of responseData.included) { + identities[data.id as string] = data; + } + + if (!Array.isArray(responseData.data)) { + responseData.data = [responseData.data]; + } + + for (let i = 0; i < responseData.data.length; i++) { + for (let y = 0; y < responseData.data[i].relationships.identities.data.length; y++) { + //@ts-ignore + responseData.data[i].relationships.identities.data[y] = + identities[responseData.data[i].relationships.identities.data[y].id]; + } + } +} + +export function resolveMember(responseData: IRelation) { + const members: IDataObject = {}; + for (const data of responseData.included) { + members[data.id as string] = data; + } + + if (!Array.isArray(responseData.data)) { + responseData.data = [responseData.data]; + } + + for (let i = 0; i < responseData.data.length; i++) { + //@ts-ignore + responseData.data[i].relationships.member.data = + //@ts-ignore + members[responseData.data[i].relationships.member.data.id]; + } +} + /** * Make an API request to paginated flow endpoint * and return all results @@ -79,40 +116,3 @@ export async function orbitApiRequestAllItems( } while (responseData.data.length !== 0); return returnData; } - -export function resolveIdentities(responseData: IRelation) { - const identities: IDataObject = {}; - for (const data of responseData.included) { - identities[data.id as string] = data; - } - - if (!Array.isArray(responseData.data)) { - responseData.data = [responseData.data]; - } - - for (let i = 0; i < responseData.data.length; i++) { - for (let y = 0; y < responseData.data[i].relationships.identities.data.length; y++) { - //@ts-ignore - responseData.data[i].relationships.identities.data[y] = - identities[responseData.data[i].relationships.identities.data[y].id]; - } - } -} - -export function resolveMember(responseData: IRelation) { - const members: IDataObject = {}; - for (const data of responseData.included) { - members[data.id as string] = data; - } - - if (!Array.isArray(responseData.data)) { - responseData.data = [responseData.data]; - } - - for (let i = 0; i < responseData.data.length; i++) { - //@ts-ignore - responseData.data[i].relationships.member.data = - //@ts-ignore - members[responseData.data[i].relationships.member.data.id]; - } -} diff --git a/packages/nodes-base/nodes/Oura/GenericFunctions.ts b/packages/nodes-base/nodes/Oura/GenericFunctions.ts index 4f3655c5e8..3c8bf14e48 100644 --- a/packages/nodes-base/nodes/Oura/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Oura/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function ouraApiRequest( method, qs, body, - uri: uri || `https://api.ouraring.com/v1${resource}`, + uri: uri ?? `https://api.ouraring.com/v1${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/PagerDuty/GenericFunctions.ts b/packages/nodes-base/nodes/PagerDuty/GenericFunctions.ts index 3a2cb7ee73..5fc2299498 100644 --- a/packages/nodes-base/nodes/PagerDuty/GenericFunctions.ts +++ b/packages/nodes-base/nodes/PagerDuty/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function pagerDutyApiRequest( method, body, qs: query, - uri: uri || `https://api.pagerduty.com${resource}`, + uri: uri ?? `https://api.pagerduty.com${resource}`, json: true, qsStringifyOptions: { arrayFormat: 'brackets', diff --git a/packages/nodes-base/nodes/PayPal/GenericFunctions.ts b/packages/nodes-base/nodes/PayPal/GenericFunctions.ts index 63bd6d2a22..1d7c2fc3d7 100644 --- a/packages/nodes-base/nodes/PayPal/GenericFunctions.ts +++ b/packages/nodes-base/nodes/PayPal/GenericFunctions.ts @@ -11,44 +11,7 @@ import { import { IDataObject, NodeApiError, NodeOperationError } from 'n8n-workflow'; -export async function payPalApiRequest( - this: - | IHookFunctions - | IExecuteFunctions - | IExecuteSingleFunctions - | ILoadOptionsFunctions - | IWebhookFunctions, - endpoint: string, - method: string, - - body: any = {}, - query?: IDataObject, - uri?: string, -): Promise { - const credentials = await this.getCredentials('payPalApi'); - const env = getEnvironment(credentials.env as string); - const tokenInfo = await getAccessToken.call(this); - const headerWithAuthentication = Object.assign( - {}, - { Authorization: `Bearer ${tokenInfo.access_token}`, 'Content-Type': 'application/json' }, - ); - const options = { - headers: headerWithAuthentication, - method, - qs: query || {}, - uri: uri || `${env}/v1${endpoint}`, - body, - json: true, - }; - try { - return await this.helpers.request(options); - } catch (error) { - throw new NodeApiError(this.getNode(), error); - } -} - -function getEnvironment(env: string): string { - // @ts-ignore +function getEnvironment(env: string) { return { sanbox: 'https://api-m.sandbox.paypal.com', live: 'https://api-m.paypal.com', @@ -88,6 +51,51 @@ async function getAccessToken( } } +export async function payPalApiRequest( + this: + | IHookFunctions + | IExecuteFunctions + | IExecuteSingleFunctions + | ILoadOptionsFunctions + | IWebhookFunctions, + endpoint: string, + method: string, + + body: any = {}, + query?: IDataObject, + uri?: string, +): Promise { + const credentials = await this.getCredentials('payPalApi'); + const env = getEnvironment(credentials.env as string); + const tokenInfo = await getAccessToken.call(this); + const headerWithAuthentication = Object.assign( + {}, + { Authorization: `Bearer ${tokenInfo.access_token}`, 'Content-Type': 'application/json' }, + ); + const options = { + headers: headerWithAuthentication, + method, + qs: query ?? {}, + uri: uri ?? `${env}/v1${endpoint}`, + body, + json: true, + }; + try { + return await this.helpers.request(options); + } catch (error) { + throw new NodeApiError(this.getNode(), error); + } +} + +function getNext(links: IDataObject[]): string | undefined { + for (const link of links) { + if (link.rel === 'next') { + return link.href as string; + } + } + return undefined; +} + /** * Make an API request to paginated paypal endpoint * and return all results @@ -117,15 +125,6 @@ export async function payPalApiRequestAllItems( return returnData; } -function getNext(links: IDataObject[]): string | undefined { - for (const link of links) { - if (link.rel === 'next') { - return link.href as string; - } - } - return undefined; -} - export function validateJSON(json: string | undefined): any { let result; try { diff --git a/packages/nodes-base/nodes/Peekalink/GenericFunctions.ts b/packages/nodes-base/nodes/Peekalink/GenericFunctions.ts index 5a5a4ce3ee..86f19db6ca 100644 --- a/packages/nodes-base/nodes/Peekalink/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Peekalink/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function peekalinkApiRequest( method, qs, body, - uri: uri || `https://api.peekalink.io${resource}`, + uri: uri ?? `https://api.peekalink.io${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/PhilipsHue/GenericFunctions.ts b/packages/nodes-base/nodes/PhilipsHue/GenericFunctions.ts index f1f5cbe891..88bdd0fa64 100644 --- a/packages/nodes-base/nodes/PhilipsHue/GenericFunctions.ts +++ b/packages/nodes-base/nodes/PhilipsHue/GenericFunctions.ts @@ -21,7 +21,7 @@ export async function philipsHueApiRequest( method, body, qs, - uri: uri || `https://api.meethue.com/route${resource}`, + uri: uri ?? `https://api.meethue.com/route${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/PostBin/GenericFunctions.ts b/packages/nodes-base/nodes/PostBin/GenericFunctions.ts index b396453e8f..52206860fd 100644 --- a/packages/nodes-base/nodes/PostBin/GenericFunctions.ts +++ b/packages/nodes-base/nodes/PostBin/GenericFunctions.ts @@ -9,6 +9,36 @@ import { // Regular expressions used to extract binId from parameter value const BIN_ID_REGEX = /\b\d{13}-\d{13}\b/g; +/** + * Extracts the PostBin Bin Id from the specified string. + * This method should be able to extract bin Id from the + * PostBin URL or from the string in the following format: + * `Bin ''.` + * + */ +function parseBinId(context: IExecuteSingleFunctions) { + const binId = context.getNodeParameter('binId') as string; + // Test if the Bin id is in the expected format + BIN_ID_REGEX.lastIndex = 0; + const idMatch = BIN_ID_REGEX.exec(binId); + + // Return what is matched + if (idMatch) { + return idMatch[0]; + } + + // If it's not recognized, error out + throw new NodeApiError( + context.getNode(), + {}, + { + message: 'Bin ID format is not valid', + description: 'Please check the provided Bin ID and try again.', + parseXml: false, + }, + ); +} + /** * Creates correctly-formatted PostBin API URL based on the entered binId. * This function makes sure binId is in the expected format by parsing it @@ -60,36 +90,6 @@ export async function buildRequestURL( return requestOptions; } -/** - * Extracts the PostBin Bin Id from the specified string. - * This method should be able to extract bin Id from the - * PostBin URL or from the string in the following format: - * `Bin ''.` - * - */ -function parseBinId(context: IExecuteSingleFunctions) { - const binId = context.getNodeParameter('binId') as string; - // Test if the Bin id is in the expected format - BIN_ID_REGEX.lastIndex = 0; - const idMatch = BIN_ID_REGEX.exec(binId); - - // Return what is matched - if (idMatch) { - return idMatch[0]; - } - - // If it's not recognized, error out - throw new NodeApiError( - context.getNode(), - {}, - { - message: 'Bin ID format is not valid', - description: 'Please check the provided Bin ID and try again.', - parseXml: false, - }, - ); -} - /** * Converts the bin response data and adds additional properties * @@ -107,8 +107,8 @@ export async function transformBinReponse( nowIso: new Date(item.json.now as string).toISOString(), expiresTimestamp: item.json.expires, expiresIso: new Date(item.json.expires as string).toISOString(), - requestUrl: 'https://www.toptal.com/developers/postbin/' + item.json.binId, - viewUrl: 'https://www.toptal.com/developers/postbin/b/' + item.json.binId, + requestUrl: 'https://www.toptal.com/developers/postbin/' + (item.json.binId as string), + viewUrl: 'https://www.toptal.com/developers/postbin/b/' + (item.json.binId as string), }), ); return items; diff --git a/packages/nodes-base/nodes/Postgres/Postgres.node.functions.ts b/packages/nodes-base/nodes/Postgres/Postgres.node.functions.ts index 615d547e8f..1714a586d3 100644 --- a/packages/nodes-base/nodes/Postgres/Postgres.node.functions.ts +++ b/packages/nodes-base/nodes/Postgres/Postgres.node.functions.ts @@ -491,7 +491,7 @@ export async function pgUpdate( const returning = generateReturning(pgp, getNodeParam('returnFields', 0) as string); if (mode === 'multiple') { const query = - pgp.helpers.update(updateItems, cs) + + (pgp.helpers.update(updateItems, cs) as string) + ' WHERE ' + updateKeys .map((entry) => { @@ -515,7 +515,9 @@ export async function pgUpdate( Array.prototype.push.apply( result, await t.any( - pgp.helpers.update(itemCopy, cs) + pgp.as.format(where, itemCopy) + returning, + (pgp.helpers.update(itemCopy, cs) as string) + + pgp.as.format(where, itemCopy) + + returning, ), ); } catch (err) { @@ -539,7 +541,9 @@ export async function pgUpdate( Array.prototype.push.apply( result, await t.any( - pgp.helpers.update(itemCopy, cs) + pgp.as.format(where, itemCopy) + returning, + (pgp.helpers.update(itemCopy, cs) as string) + + pgp.as.format(where, itemCopy) + + returning, ), ); } catch (err) { @@ -612,7 +616,7 @@ export async function pgUpdateV2( const returning = generateReturning(pgp, this.getNodeParameter('returnFields', 0) as string); if (mode === 'multiple') { const query = - pgp.helpers.update(updateItems, cs) + + (pgp.helpers.update(updateItems, cs) as string) + ' WHERE ' + updateKeys .map((entry) => { @@ -635,7 +639,9 @@ export async function pgUpdateV2( const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); try { const transactionResult = await t.any( - pgp.helpers.update(itemCopy, cs) + pgp.as.format(where, itemCopy) + returning, + (pgp.helpers.update(itemCopy, cs) as string) + + pgp.as.format(where, itemCopy) + + returning, ); const executionData = this.helpers.constructExecutionMetaData( this.helpers.returnJsonArray(transactionResult), @@ -661,7 +667,9 @@ export async function pgUpdateV2( const itemCopy = getItemCopy(items[i], columnNames, guardedColumns); try { const independentResult = await t.any( - pgp.helpers.update(itemCopy, cs) + pgp.as.format(where, itemCopy) + returning, + (pgp.helpers.update(itemCopy, cs) as string) + + pgp.as.format(where, itemCopy) + + returning, ); const executionData = this.helpers.constructExecutionMetaData( this.helpers.returnJsonArray(independentResult), diff --git a/packages/nodes-base/nodes/ProfitWell/GenericFunctions.ts b/packages/nodes-base/nodes/ProfitWell/GenericFunctions.ts index 64d338ea9f..73ce29f581 100644 --- a/packages/nodes-base/nodes/ProfitWell/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ProfitWell/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function profitWellApiRequest( method, qs, body, - uri: uri || `https://api.profitwell.com/v2${resource}`, + uri: uri ?? `https://api.profitwell.com/v2${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Pushbullet/GenericFunctions.ts b/packages/nodes-base/nodes/Pushbullet/GenericFunctions.ts index 5dc7a7dcc4..120243966c 100644 --- a/packages/nodes-base/nodes/Pushbullet/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Pushbullet/GenericFunctions.ts @@ -18,7 +18,7 @@ export async function pushbulletApiRequest( method, body, qs, - uri: uri || `https://api.pushbullet.com/v2${path}`, + uri: uri ?? `https://api.pushbullet.com/v2${path}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Pushcut/GenericFunctions.ts b/packages/nodes-base/nodes/Pushcut/GenericFunctions.ts index b29003d7e7..0612d38cc2 100644 --- a/packages/nodes-base/nodes/Pushcut/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Pushcut/GenericFunctions.ts @@ -23,7 +23,7 @@ export async function pushcutApiRequest( method, body, qs, - uri: uri || `https://api.pushcut.io/v1${path}`, + uri: uri ?? `https://api.pushcut.io/v1${path}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/QuestDb/QuestDb.node.ts b/packages/nodes-base/nodes/QuestDb/QuestDb.node.ts index b041a2b166..6a61260839 100644 --- a/packages/nodes-base/nodes/QuestDb/QuestDb.node.ts +++ b/packages/nodes-base/nodes/QuestDb/QuestDb.node.ts @@ -219,7 +219,7 @@ export class QuestDb implements INodeType { // ---------------------------------- const additionalFields = this.getNodeParameter('additionalFields', 0); - const mode = (additionalFields.mode || 'independently') as string; + const mode = (additionalFields.mode ?? 'independently') as string; const queryResult = await pgQuery( this.getNodeParameter, diff --git a/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts b/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts index 74befffa75..646e906c9b 100644 --- a/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts +++ b/packages/nodes-base/nodes/QuickBooks/GenericFunctions.ts @@ -90,6 +90,17 @@ export async function quickBooksApiRequest( } } +async function getCount( + this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, + method: string, + endpoint: string, + qs: IDataObject, +): Promise { + const responseData = await quickBooksApiRequest.call(this, method, endpoint, qs, {}); + + return responseData.QueryResponse.totalCount; +} + /** * Make an authenticated API request to QuickBooks and return all results. */ @@ -134,17 +145,6 @@ export async function quickBooksApiRequestAllItems( return returnData; } -async function getCount( - this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, - method: string, - endpoint: string, - qs: IDataObject, -): Promise { - const responseData = await quickBooksApiRequest.call(this, method, endpoint, qs, {}); - - return responseData.QueryResponse.totalCount; -} - /** * Handles a QuickBooks listing by returning all items or up to a limit. */ @@ -427,14 +427,14 @@ export function populateFields( export const toOptions = (option: string) => ({ name: option, value: option }); -export const toDisplayName = ({ name, value }: Option): INodePropertyOptions => { - return { name: splitPascalCase(name), value }; -}; - export const splitPascalCase = (word: string) => { return word.match(/($[a-z])|[A-Z][^A-Z]+/g)!.join(' '); }; +export const toDisplayName = ({ name, value }: Option): INodePropertyOptions => { + return { name: splitPascalCase(name), value }; +}; + export function adjustTransactionDates(transactionFields: IDataObject & DateFieldsUi): IDataObject { const dateFieldKeys = [ 'dateRangeCustom', diff --git a/packages/nodes-base/nodes/RabbitMQ/RabbitMQTrigger.node.ts b/packages/nodes-base/nodes/RabbitMQ/RabbitMQTrigger.node.ts index 7f36dea037..9f152152c3 100644 --- a/packages/nodes-base/nodes/RabbitMQ/RabbitMQTrigger.node.ts +++ b/packages/nodes-base/nodes/RabbitMQ/RabbitMQTrigger.node.ts @@ -149,8 +149,6 @@ export class RabbitMQTrigger implements INodeType { const channel = await rabbitmqConnectQueue.call(this, queue, options); - const self = this; - let parallelMessages = options.parallelMessages !== undefined && options.parallelMessages !== -1 ? parseInt(options.parallelMessages as string, 10) @@ -188,7 +186,7 @@ export class RabbitMQTrigger implements INodeType { channel.on('close', () => { if (!closeGotCalled) { - self.emitError(new Error('Connection got closed unexpectedly')); + this.emitError(new Error('Connection got closed unexpectedly')); } }); @@ -229,7 +227,7 @@ export class RabbitMQTrigger implements INodeType { responsePromise = await createDeferredPromise(); } - self.emit([[item]], undefined, responsePromise); + this.emit([[item]], undefined, responsePromise); if (responsePromise) { // Acknowledge message after the execution finished @@ -274,13 +272,13 @@ export class RabbitMQTrigger implements INodeType { // The "closeFunction" function gets called by n8n whenever // the workflow gets deactivated and can so clean up. - async function closeFunction() { + const closeFunction = async () => { closeGotCalled = true; try { return await messageTracker.closeChannel(channel, consumerTag); } catch (error) { - const workflow = self.getWorkflow(); - const node = self.getNode(); + const workflow = this.getWorkflow(); + const node = this.getNode(); Logger.error( `There was a problem closing the RabbitMQ Trigger node connection "${node.name}" in workflow "${workflow.id}": "${error.message}"`, { @@ -289,7 +287,7 @@ export class RabbitMQTrigger implements INodeType { }, ); } - } + }; return { closeFunction, diff --git a/packages/nodes-base/nodes/ReadPdf/ReadPDF.node.ts b/packages/nodes-base/nodes/ReadPdf/ReadPDF.node.ts index 0d234beba8..104dcebf5a 100644 --- a/packages/nodes-base/nodes/ReadPdf/ReadPDF.node.ts +++ b/packages/nodes-base/nodes/ReadPdf/ReadPDF.node.ts @@ -1,8 +1,8 @@ import { IExecuteFunctions } from 'n8n-core'; -import { INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow'; +import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow'; -const pdf = require('pdf-parse'); +import pdf from 'pdf-parse'; export class ReadPDF implements INodeType { description: INodeTypeDescription = { @@ -50,7 +50,8 @@ export class ReadPDF implements INodeType { const binaryData = await this.helpers.getBinaryDataBuffer(itemIndex, binaryPropertyName); returnData.push({ binary: item.binary, - json: await pdf(binaryData), + + json: (await pdf(binaryData)) as unknown as IDataObject, }); } catch (error) { if (this.continueOnFail()) { diff --git a/packages/nodes-base/nodes/Reddit/Reddit.node.ts b/packages/nodes-base/nodes/Reddit/Reddit.node.ts index ef282016b9..9a57f05967 100644 --- a/packages/nodes-base/nodes/Reddit/Reddit.node.ts +++ b/packages/nodes-base/nodes/Reddit/Reddit.node.ts @@ -138,7 +138,7 @@ export class Reddit implements INodeType { const postTypePrefix = 't3_'; const qs: IDataObject = { - id: postTypePrefix + this.getNodeParameter('postId', i), + id: postTypePrefix + (this.getNodeParameter('postId', i) as string), }; await redditApiRequest.call(this, 'POST', 'api/del', qs); @@ -228,7 +228,7 @@ export class Reddit implements INodeType { const qs: IDataObject = { text: this.getNodeParameter('commentText', i), - thing_id: postTypePrefix + this.getNodeParameter('postId', i), + thing_id: postTypePrefix + (this.getNodeParameter('postId', i) as string), }; responseData = await redditApiRequest.call(this, 'POST', 'api/comment', qs); @@ -255,7 +255,7 @@ export class Reddit implements INodeType { const commentTypePrefix = 't1_'; const qs: IDataObject = { - id: commentTypePrefix + this.getNodeParameter('commentId', i), + id: commentTypePrefix + (this.getNodeParameter('commentId', i) as string), }; await redditApiRequest.call(this, 'POST', 'api/del', qs); @@ -272,7 +272,7 @@ export class Reddit implements INodeType { const qs: IDataObject = { text: this.getNodeParameter('replyText', i), - thing_id: commentTypePrefix + this.getNodeParameter('commentId', i), + thing_id: commentTypePrefix + (this.getNodeParameter('commentId', i) as string), }; responseData = await redditApiRequest.call(this, 'POST', 'api/comment', qs); diff --git a/packages/nodes-base/nodes/Redis/Redis.node.ts b/packages/nodes-base/nodes/Redis/Redis.node.ts index e6363245f0..2038926ba5 100644 --- a/packages/nodes-base/nodes/Redis/Redis.node.ts +++ b/packages/nodes-base/nodes/Redis/Redis.node.ts @@ -506,9 +506,8 @@ export class Redis implements INodeType { } try { const client = redis.createClient(redisOptions); - // tslint:disable-next-line: no-any - const _data = await new Promise((resolve, reject): any => { + await new Promise((resolve, reject): any => { client.on('connect', async () => { client.ping('ping', (error, pong) => { if (error) reject(error); @@ -703,7 +702,7 @@ export class Redis implements INodeType { const keyGet = this.getNodeParameter('key', itemIndex) as string; const keyType = this.getNodeParameter('keyType', itemIndex) as string; - const value = (await getValue(client, keyGet, keyType)) || null; + const value = (await getValue(client, keyGet, keyType)) ?? null; const options = this.getNodeParameter('options', itemIndex, {}); diff --git a/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts b/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts index 60b9c23964..12b3e9e014 100644 --- a/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts +++ b/packages/nodes-base/nodes/Redis/RedisTrigger.node.ts @@ -88,9 +88,7 @@ export class RedisTrigger implements INodeType { const client = redis.createClient(redisOptions); - const self = this; - - async function manualTriggerFunction() { + const manualTriggerFunction = async () => { await new Promise((resolve, reject) => { client.on('connect', () => { for (const channel of channels) { @@ -104,12 +102,12 @@ export class RedisTrigger implements INodeType { } if (options.onlyMessage) { - self.emit([self.helpers.returnJsonArray({ message })]); + this.emit([this.helpers.returnJsonArray({ message })]); resolve(true); return; } - self.emit([self.helpers.returnJsonArray({ channel, message })]); + this.emit([this.helpers.returnJsonArray({ channel, message })]); resolve(true); }); }); @@ -118,7 +116,7 @@ export class RedisTrigger implements INodeType { reject(error); }); }); - } + }; if (this.getMode() === 'trigger') { await manualTriggerFunction(); diff --git a/packages/nodes-base/nodes/RssFeedRead/RssFeedRead.node.ts b/packages/nodes-base/nodes/RssFeedRead/RssFeedRead.node.ts index 7b43fcd543..8b0b50d738 100644 --- a/packages/nodes-base/nodes/RssFeedRead/RssFeedRead.node.ts +++ b/packages/nodes-base/nodes/RssFeedRead/RssFeedRead.node.ts @@ -10,6 +10,17 @@ import { import Parser from 'rss-parser'; import { URL } from 'url'; +// Utility function + +function validateURL(url: string) { + try { + const _parseUrl = new URL(url); + return true; + } catch (err) { + return false; + } +} + export class RssFeedRead implements INodeType { description: INodeTypeDescription = { displayName: 'RSS Read', @@ -83,14 +94,3 @@ export class RssFeedRead implements INodeType { } } } - -// Utility function - -function validateURL(url: string) { - try { - const _parseUrl = new URL(url); - return true; - } catch (err) { - return false; - } -} diff --git a/packages/nodes-base/nodes/Rundeck/RundeckApi.ts b/packages/nodes-base/nodes/Rundeck/RundeckApi.ts index 2e8c8856a8..d008240d78 100644 --- a/packages/nodes-base/nodes/Rundeck/RundeckApi.ts +++ b/packages/nodes-base/nodes/Rundeck/RundeckApi.ts @@ -23,7 +23,7 @@ export class RundeckApi { rejectUnauthorized: false, method, qs: query, - uri: this.credentials?.url + endpoint, + uri: (this.credentials?.url as string) + endpoint, body, json: true, }; @@ -54,7 +54,7 @@ export class RundeckApi { if (args) { for (const arg of args) { - params += '-' + arg.name + ' ' + arg.value + ' '; + params += '-' + (arg.name as string) + ' ' + (arg.value as string) + ' '; } } diff --git a/packages/nodes-base/nodes/S3/GenericFunctions.ts b/packages/nodes-base/nodes/S3/GenericFunctions.ts index 553900e6be..dfca7eb62b 100644 --- a/packages/nodes-base/nodes/S3/GenericFunctions.ts +++ b/packages/nodes-base/nodes/S3/GenericFunctions.ts @@ -17,6 +17,12 @@ import { IDataObject, NodeApiError, NodeOperationError } from 'n8n-workflow'; import { URL } from 'url'; +function queryToString(params: IDataObject) { + return Object.keys(params) + .map((key) => key + '=' + (params[key] as string)) + .join('&'); +} + export async function s3ApiRequest( this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions | IWebhookFunctions, bucket: string, @@ -51,8 +57,8 @@ export async function s3ApiRequest( // Sign AWS API request with the user credentials const signOpts = { - headers: headers || {}, - region: region || credentials.region, + headers: headers ?? {}, + region: region ?? credentials.region, host: endpoint.host, method, path: `${path}?${queryToString(query).replace(/\+/g, '%2B')}`, @@ -206,9 +212,3 @@ export async function s3ApiRequestSOAPAllItems( return returnData; } - -function queryToString(params: IDataObject) { - return Object.keys(params) - .map((key) => key + '=' + params[key]) - .join('&'); -} diff --git a/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts b/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts index fd679407dd..3ed5870f1a 100644 --- a/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Salesforce/GenericFunctions.ts @@ -13,104 +13,6 @@ import moment from 'moment-timezone'; import jwt from 'jsonwebtoken'; -export async function salesforceApiRequest( - this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, - method: string, - endpoint: string, - - body: any = {}, - qs: IDataObject = {}, - uri?: string, - option: IDataObject = {}, -): Promise { - const authenticationMethod = this.getNodeParameter('authentication', 0, 'oAuth2') as string; - try { - if (authenticationMethod === 'jwt') { - // https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm&type=5 - const credentialsType = 'salesforceJwtApi'; - const credentials = await this.getCredentials(credentialsType); - const response = await getAccessToken.call(this, credentials); - const { instance_url, access_token } = response; - const options = getOptions.call( - this, - method, - uri || endpoint, - body, - qs, - instance_url as string, - ); - Logger.debug( - `Authentication for "Salesforce" node is using "jwt". Invoking URI ${options.uri}`, - ); - options.headers!.Authorization = `Bearer ${access_token}`; - Object.assign(options, option); - //@ts-ignore - return await this.helpers.request(options); - } else { - // https://help.salesforce.com/articleView?id=remoteaccess_oauth_web_server_flow.htm&type=5 - const credentialsType = 'salesforceOAuth2Api'; - const credentials = (await this.getCredentials(credentialsType)) as { - oauthTokenData: { instance_url: string }; - }; - const options = getOptions.call( - this, - method, - uri || endpoint, - body, - qs, - credentials.oauthTokenData.instance_url, - ); - Logger.debug( - `Authentication for "Salesforce" node is using "OAuth2". Invoking URI ${options.uri}`, - ); - Object.assign(options, option); - //@ts-ignore - return await this.helpers.requestOAuth2.call(this, credentialsType, options); - } - } catch (error) { - throw new NodeApiError(this.getNode(), error); - } -} - -export async function salesforceApiRequestAllItems( - this: IExecuteFunctions | ILoadOptionsFunctions, - propertyName: string, - method: string, - endpoint: string, - - body: any = {}, - query: IDataObject = {}, -): Promise { - const returnData: IDataObject[] = []; - - let responseData; - let uri: string | undefined; - - do { - responseData = await salesforceApiRequest.call(this, method, endpoint, body, query, uri); - uri = `${endpoint}/${responseData.nextRecordsUrl?.split('/')?.pop()}`; - returnData.push.apply(returnData, responseData[propertyName]); - } while (responseData.nextRecordsUrl !== undefined && responseData.nextRecordsUrl !== null); - - return returnData; -} - -/** - * Sorts the given options alphabetically - * - */ -export function sortOptions(options: INodePropertyOptions[]): void { - options.sort((a, b) => { - if (a.name < b.name) { - return -1; - } - if (a.name > b.name) { - return 1; - } - return 0; - }); -} - function getOptions( this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -181,6 +83,114 @@ async function getAccessToken( return this.helpers.request(options); } +export async function salesforceApiRequest( + this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, + method: string, + endpoint: string, + + body: any = {}, + qs: IDataObject = {}, + uri?: string, + option: IDataObject = {}, +): Promise { + const authenticationMethod = this.getNodeParameter('authentication', 0, 'oAuth2') as string; + try { + if (authenticationMethod === 'jwt') { + // https://help.salesforce.com/articleView?id=remoteaccess_oauth_jwt_flow.htm&type=5 + const credentialsType = 'salesforceJwtApi'; + const credentials = await this.getCredentials(credentialsType); + const response = await getAccessToken.call(this, credentials); + const { instance_url, access_token } = response; + const options = getOptions.call( + this, + method, + uri ?? endpoint, + body, + qs, + instance_url as string, + ); + Logger.debug( + `Authentication for "Salesforce" node is using "jwt". Invoking URI ${options.uri}`, + ); + options.headers!.Authorization = `Bearer ${access_token}`; + Object.assign(options, option); + //@ts-ignore + return await this.helpers.request(options); + } else { + // https://help.salesforce.com/articleView?id=remoteaccess_oauth_web_server_flow.htm&type=5 + const credentialsType = 'salesforceOAuth2Api'; + const credentials = (await this.getCredentials(credentialsType)) as { + oauthTokenData: { instance_url: string }; + }; + const options = getOptions.call( + this, + method, + uri ?? endpoint, + body, + qs, + credentials.oauthTokenData.instance_url, + ); + Logger.debug( + `Authentication for "Salesforce" node is using "OAuth2". Invoking URI ${options.uri}`, + ); + Object.assign(options, option); + //@ts-ignore + return await this.helpers.requestOAuth2.call(this, credentialsType, options); + } + } catch (error) { + throw new NodeApiError(this.getNode(), error); + } +} + +export async function salesforceApiRequestAllItems( + this: IExecuteFunctions | ILoadOptionsFunctions, + propertyName: string, + method: string, + endpoint: string, + + body: any = {}, + query: IDataObject = {}, +): Promise { + const returnData: IDataObject[] = []; + + let responseData; + let uri: string | undefined; + + do { + responseData = await salesforceApiRequest.call(this, method, endpoint, body, query, uri); + uri = `${endpoint}/${responseData.nextRecordsUrl?.split('/')?.pop()}`; + returnData.push.apply(returnData, responseData[propertyName]); + } while (responseData.nextRecordsUrl !== undefined && responseData.nextRecordsUrl !== null); + + return returnData; +} + +/** + * Sorts the given options alphabetically + * + */ +export function sortOptions(options: INodePropertyOptions[]): void { + options.sort((a, b) => { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + return 0; + }); +} + +export function getValue(value: any) { + if (moment(value).isValid()) { + return value; + } else if (typeof value === 'string') { + return `'${value}'`; + } else { + return value; + } +} + export function getConditions(options: IDataObject) { const conditions = (options.conditionsUi as IDataObject)?.conditionValues as IDataObject[]; let data = undefined; @@ -235,13 +245,3 @@ export function getQuery(options: IDataObject, sobject: string, returnAll: boole return query; } - -export function getValue(value: any) { - if (moment(value).isValid()) { - return value; - } else if (typeof value === 'string') { - return `'${value}'`; - } else { - return value; - } -} diff --git a/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts b/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts index d9036c2c71..3c5e081da8 100644 --- a/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts +++ b/packages/nodes-base/nodes/Salesforce/Salesforce.node.ts @@ -303,7 +303,7 @@ export class Salesforce implements INodeType { const userName = user.Name; const userId = user.Id; returnData.push({ - name: userPrefix + userName, + name: userPrefix + (userName as string), value: userId, }); } @@ -349,7 +349,7 @@ export class Salesforce implements INodeType { const userName = user.Name; const userId = user.Id; returnData.push({ - name: userPrefix + userName, + name: userPrefix + (userName as string), value: userId, }); } @@ -1854,7 +1854,7 @@ export class Salesforce implements INodeType { const dataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName); body.entity_content.PathOnClient = `${title}.${ - additionalFields.fileExtension || binaryData.fileExtension + additionalFields.fileExtension ?? binaryData.fileExtension }`; data = { entity_content: { diff --git a/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts b/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts index fcfad002d9..f955040bc0 100644 --- a/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Salesmate/GenericFunctions.ts @@ -34,7 +34,7 @@ export async function salesmateApiRequest( method, qs, body, - uri: uri || `https://apis.salesmate.io${resource}`, + uri: uri ?? `https://apis.salesmate.io${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/nodes/SeaTable/GenericFunctions.ts b/packages/nodes-base/nodes/SeaTable/GenericFunctions.ts index 536e28771a..1723af341a 100644 --- a/packages/nodes-base/nodes/SeaTable/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SeaTable/GenericFunctions.ts @@ -20,6 +20,56 @@ import { import _ from 'lodash'; +const userBaseUri = (uri?: string) => { + if (uri === undefined) { + return uri; + } + + if (uri.endsWith('/')) { + return uri.slice(0, -1); + } + + return uri; +}; + +export function resolveBaseUri(ctx: ICtx) { + return ctx?.credentials?.environment === 'cloudHosted' + ? 'https://cloud.seatable.io' + : userBaseUri(ctx?.credentials?.domain); +} + +export async function getBaseAccessToken( + this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions, + ctx: ICtx, +) { + if (ctx?.base?.access_token !== undefined) { + return; + } + + const options: OptionsWithUri = { + headers: { + Authorization: `Token ${ctx?.credentials?.token}`, + }, + uri: `${resolveBaseUri(ctx)}/api/v2.1/dtable/app-access-token/`, + json: true, + }; + + ctx.base = await this.helpers.request(options); +} + +function endpointCtxExpr(ctx: ICtx, endpoint: string): string { + const endpointVariables: IEndpointVariables = {}; + endpointVariables.access_token = ctx?.base?.access_token; + endpointVariables.dtable_uuid = ctx?.base?.dtable_uuid; + + return endpoint.replace( + /({{ *(access_token|dtable_uuid|server) *}})/g, + (match: string, expr: string, name: TEndpointVariableName) => { + return endpointVariables[name] ?? match; + }, + ); +} + export async function seaTableApiRequest( this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions, ctx: ICtx, @@ -44,7 +94,7 @@ export async function seaTableApiRequest( method, qs, body, - uri: url || `${resolveBaseUri(ctx)}${endpointCtxExpr(ctx, endpoint)}`, + uri: url ?? `${resolveBaseUri(ctx)}${endpointCtxExpr(ctx, endpoint)}`, json: true, }; @@ -57,7 +107,6 @@ export async function seaTableApiRequest( } try { - //@ts-ignore return await this.helpers.request(options); } catch (error) { throw new NodeApiError(this.getNode(), error); @@ -138,31 +187,6 @@ export async function getTableViews( return views; } -export async function getBaseAccessToken( - this: IExecuteFunctions | ILoadOptionsFunctions | IPollFunctions, - ctx: ICtx, -) { - if (ctx?.base?.access_token !== undefined) { - return; - } - - const options: OptionsWithUri = { - headers: { - Authorization: `Token ${ctx?.credentials?.token}`, - }, - uri: `${resolveBaseUri(ctx)}/api/v2.1/dtable/app-access-token/`, - json: true, - }; - - ctx.base = await this.helpers.request(options); -} - -export function resolveBaseUri(ctx: ICtx) { - return ctx?.credentials?.environment === 'cloudHosted' - ? 'https://cloud.seatable.io' - : userBaseUri(ctx?.credentials?.domain); -} - export function simplify(data: { results: IRow[] }, metadata: IDataObject) { return data.results.map((row: IDataObject) => { for (const key of Object.keys(row)) { @@ -195,6 +219,14 @@ const namePredicate = (name: string) => (named: IName) => named.name === name; export const nameOfPredicate = (names: readonly IName[]) => (name: string) => names.find(namePredicate(name)); +const normalize = (subject: string): string => (subject ? subject.normalize() : ''); + +export const split = (subject: string): string[] => + normalize(subject) + .split(/\s*((?:[^\\,]*?(?:\\[\s\S])*)*?)\s*(?:,|$)/) + .filter((s) => s.length) + .map((s) => s.replace(/\\([\s\S])/gm, ($0, $1) => $1)); + export function columnNamesToArray(columnNames: string): string[] { return columnNames ? split(columnNames).filter(nonInternalPredicate).filter(uniquePredicate) : []; } @@ -308,36 +340,3 @@ export const dtableSchemaColumns = (columns: TDtableMetadataColumns): TDtableMet export const updateAble = (columns: TDtableMetadataColumns): TDtableMetadataColumns => columns.filter(dtableSchemaIsUpdateAbleColumn); - -function endpointCtxExpr(ctx: ICtx, endpoint: string): string { - const endpointVariables: IEndpointVariables = {}; - endpointVariables.access_token = ctx?.base?.access_token; - endpointVariables.dtable_uuid = ctx?.base?.dtable_uuid; - - return endpoint.replace( - /({{ *(access_token|dtable_uuid|server) *}})/g, - (match: string, expr: string, name: TEndpointVariableName) => { - return endpointVariables[name] || match; - }, - ); -} - -const normalize = (subject: string): string => (subject ? subject.normalize() : ''); - -export const split = (subject: string): string[] => - normalize(subject) - .split(/\s*((?:[^\\,]*?(?:\\[\s\S])*)*?)\s*(?:,|$)/) - .filter((s) => s.length) - .map((s) => s.replace(/\\([\s\S])/gm, ($0, $1) => $1)); - -const userBaseUri = (uri?: string) => { - if (uri === undefined) { - return uri; - } - - if (uri.endsWith('/')) { - return uri.slice(0, -1); - } - - return uri; -}; diff --git a/packages/nodes-base/nodes/SecurityScorecard/GenericFunctions.ts b/packages/nodes-base/nodes/SecurityScorecard/GenericFunctions.ts index 20d5909f59..057aae770c 100644 --- a/packages/nodes-base/nodes/SecurityScorecard/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SecurityScorecard/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function scorecardApiRequest( headers: headerWithAuthentication, method, qs: query, - uri: uri || `https://api.securityscorecard.io/${resource}`, + uri: uri ?? `https://api.securityscorecard.io/${resource}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/SecurityScorecard/SecurityScorecard.node.ts b/packages/nodes-base/nodes/SecurityScorecard/SecurityScorecard.node.ts index ab2a5c8fa3..dc1e5ff7a6 100644 --- a/packages/nodes-base/nodes/SecurityScorecard/SecurityScorecard.node.ts +++ b/packages/nodes-base/nodes/SecurityScorecard/SecurityScorecard.node.ts @@ -267,7 +267,7 @@ export class SecurityScorecard implements INodeType { body.date = this.getNodeParameter('date', i); } if (['issues', 'portfolio'].indexOf(reportType) > -1) { - body.format = this.getNodeParameter('options.format', i) || 'pdf'; + body.format = this.getNodeParameter('options.format', i) ?? 'pdf'; } if (['detailed', 'summary'].indexOf(reportType) > -1) { body.branding = this.getNodeParameter('branding', i); diff --git a/packages/nodes-base/nodes/Segment/GenericFunctions.ts b/packages/nodes-base/nodes/Segment/GenericFunctions.ts index 1ef2b7600c..a854e2093b 100644 --- a/packages/nodes-base/nodes/Segment/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Segment/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function segmentApiRequest( method, qs, body, - uri: uri || `https://api.segment.io/v1${resource}`, + uri: uri ?? `https://api.segment.io/v1${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/nodes/SendGrid/SendGrid.node.ts b/packages/nodes-base/nodes/SendGrid/SendGrid.node.ts index 3b15f105a2..f2f50cc46b 100644 --- a/packages/nodes-base/nodes/SendGrid/SendGrid.node.ts +++ b/packages/nodes-base/nodes/SendGrid/SendGrid.node.ts @@ -601,7 +601,7 @@ export class SendGrid implements INodeType { attachmentsToSend.push({ content: dataBuffer.toString('base64'), - filename: binaryProperty.fileName || 'unknown', + filename: binaryProperty.fileName ?? 'unknown', type: binaryProperty.mimeType, }); } diff --git a/packages/nodes-base/nodes/SendInBlue/GenericFunctions.ts b/packages/nodes-base/nodes/SendInBlue/GenericFunctions.ts index e8a68f7798..187832b663 100644 --- a/packages/nodes-base/nodes/SendInBlue/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SendInBlue/GenericFunctions.ts @@ -52,6 +52,24 @@ export namespace SendInBlueNode { ], ]); export namespace Validators { + function getFileName( + itemIndex: number, + mimeType: string, + fileExt: string, + fileName: string, + ): string { + let ext = fileExt; + if (fileExt === undefined) { + ext = mimeType.split('/')[1]; + } + + let name = `${fileName}.${ext}`; + if (fileName === undefined) { + name = `file-${itemIndex}.${ext}`; + } + return name; + } + export async function validateAndCompileAttachmentsData( this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions, @@ -94,7 +112,7 @@ export namespace SendInBlueNode { itemIndex, mimeType, fileExtension!, - fileName || item.binary!.data.fileName!, + fileName ?? item.binary!.data.fileName!, ); attachment.push({ content, name }); @@ -124,6 +142,69 @@ export namespace SendInBlueNode { return requestOptions; } + function formatToEmailName(data: Address): Email { + const { address: email, name } = data; + const result = { email }; + if (name !== undefined && name !== '') { + Object.assign(result, { name }); + } + return { ...result }; + } + + function validateEmailStrings(input: ValidEmailFields): ValidatedEmail { + const composer = new MailComposer({ ...input }); + const addressFields = composer.compile().getAddresses(); + + const fieldFetcher = new Map Email[] | Email>([ + [ + 'bcc', + () => { + return (addressFields.bcc as unknown as Address[])?.map(formatToEmailName); + }, + ], + [ + 'cc', + () => { + return (addressFields.cc as unknown as Address[])?.map(formatToEmailName); + }, + ], + [ + 'from', + () => { + return (addressFields.from as unknown as Address[])?.map(formatToEmailName); + }, + ], + [ + 'reply-to', + () => { + return (addressFields['reply-to'] as unknown as Address[])?.map(formatToEmailName); + }, + ], + [ + 'sender', + () => { + return (addressFields.sender as unknown as Address[])?.map(formatToEmailName)[0]; + }, + ], + [ + 'to', + () => { + return (addressFields.to as unknown as Address[])?.map(formatToEmailName); + }, + ], + ]); + + const result: { [key in keyof ValidatedEmail]: Email[] | Email } = {} as ValidatedEmail; + Object.keys(input).reduce((obj: { [key: string]: Email[] | Email }, key: string) => { + const getter = fieldFetcher.get(key); + const value = getter!(); + obj[key] = value; + return obj; + }, result); + + return result as ValidatedEmail; + } + export async function validateAndCompileCCEmails( this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions, @@ -206,87 +287,6 @@ export namespace SendInBlueNode { Object.assign(body!, { params }); return requestOptions; } - - function validateEmailStrings(input: ValidEmailFields): ValidatedEmail { - const composer = new MailComposer({ ...input }); - const addressFields = composer.compile().getAddresses(); - - const fieldFetcher = new Map Email[] | Email>([ - [ - 'bcc', - () => { - return (addressFields.bcc as unknown as Address[])?.map(formatToEmailName); - }, - ], - [ - 'cc', - () => { - return (addressFields.cc as unknown as Address[])?.map(formatToEmailName); - }, - ], - [ - 'from', - () => { - return (addressFields.from as unknown as Address[])?.map(formatToEmailName); - }, - ], - [ - 'reply-to', - () => { - return (addressFields['reply-to'] as unknown as Address[])?.map(formatToEmailName); - }, - ], - [ - 'sender', - () => { - return (addressFields.sender as unknown as Address[])?.map(formatToEmailName)[0]; - }, - ], - [ - 'to', - () => { - return (addressFields.to as unknown as Address[])?.map(formatToEmailName); - }, - ], - ]); - - const result: { [key in keyof ValidatedEmail]: Email[] | Email } = {} as ValidatedEmail; - Object.keys(input).reduce((obj: { [key: string]: Email[] | Email }, key: string) => { - const getter = fieldFetcher.get(key); - const value = getter!(); - obj[key] = value; - return obj; - }, result); - - return result as ValidatedEmail; - } - } - - function getFileName( - itemIndex: number, - mimeType: string, - fileExt: string, - fileName: string, - ): string { - let ext = fileExt; - if (fileExt === undefined) { - ext = mimeType.split('/')[1]; - } - - let name = `${fileName}.${ext}`; - if (fileName === undefined) { - name = `file-${itemIndex}.${ext}`; - } - return name; - } - - function formatToEmailName(data: Address): Email { - const { address: email, name } = data; - const result = { email }; - if (name !== undefined && name !== '') { - Object.assign(result, { name }); - } - return { ...result }; } } diff --git a/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts b/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts index 04d82d33e2..7eb309282f 100644 --- a/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts @@ -34,7 +34,7 @@ export async function sentryIoApiRequest( method, qs, body, - uri: uri || `https://sentry.io${resource}`, + uri: uri ?? `https://sentry.io${resource}`, json: true, }; if (!Object.keys(body).length) { @@ -79,6 +79,26 @@ export async function sentryIoApiRequest( } } +function getNext(link: string) { + if (link === undefined) { + return; + } + const next = link.split(',')[1]; + if (next.includes('rel="next"')) { + return next.split(';')[0].replace('<', '').replace('>', '').trim(); + } +} + +function hasMore(link: string) { + if (link === undefined) { + return; + } + const next = link.split(',')[1]; + if (next.includes('rel="next"')) { + return next.includes('results="true"'); + } +} + export async function sentryApiRequestAllItems( this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: string, @@ -109,23 +129,3 @@ export async function sentryApiRequestAllItems( return returnData; } - -function getNext(link: string) { - if (link === undefined) { - return; - } - const next = link.split(',')[1]; - if (next.includes('rel="next"')) { - return next.split(';')[0].replace('<', '').replace('>', '').trim(); - } -} - -function hasMore(link: string) { - if (link === undefined) { - return; - } - const next = link.split(',')[1]; - if (next.includes('rel="next"')) { - return next.includes('results="true"'); - } -} diff --git a/packages/nodes-base/nodes/ServiceNow/GenericFunctions.ts b/packages/nodes-base/nodes/ServiceNow/GenericFunctions.ts index 12329a7878..dea8501bec 100644 --- a/packages/nodes-base/nodes/ServiceNow/GenericFunctions.ts +++ b/packages/nodes-base/nodes/ServiceNow/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function serviceNowApiRequest( method, qs, body, - uri: uri || `https://${credentials.subdomain}.service-now.com/api${resource}`, + uri: uri ?? `https://${credentials.subdomain}.service-now.com/api${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts index 3d035fef97..ea7f25bb3c 100644 --- a/packages/nodes-base/nodes/Shopify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Shopify/GenericFunctions.ts @@ -39,7 +39,7 @@ export async function shopifyApiRequest( const options: OptionsWithUri = { method, qs: query, - uri: uri || `https://${credentials.shopSubdomain}.myshopify.com/admin/api/2019-10${resource}`, + uri: uri ?? `https://${credentials.shopSubdomain}.myshopify.com/admin/api/2019-10${resource}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/Slack/GenericFunctions.ts b/packages/nodes-base/nodes/Slack/GenericFunctions.ts index 546bc8562a..9311368847 100644 --- a/packages/nodes-base/nodes/Slack/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Slack/GenericFunctions.ts @@ -20,12 +20,11 @@ export async function slackApiRequest( query: object = {}, headers: IDataObject | undefined = undefined, option: IDataObject = {}, - // tslint:disable-next-line:no-any ): Promise { const authenticationMethod = this.getNodeParameter('authentication', 0, 'accessToken') as string; let options: OptionsWithUri = { method, - headers: headers || { + headers: headers ?? { 'Content-Type': 'application/json; charset=utf-8', }, body, diff --git a/packages/nodes-base/nodes/Splunk/GenericFunctions.ts b/packages/nodes-base/nodes/Splunk/GenericFunctions.ts index 4597ccd37a..c41c012a1e 100644 --- a/packages/nodes-base/nodes/Splunk/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Splunk/GenericFunctions.ts @@ -14,6 +14,94 @@ import { SplunkSearchResponse, } from './types'; +// ---------------------------------------- +// entry formatting +// ---------------------------------------- +function compactEntryContent(splunkObject: any): any { + if (typeof splunkObject !== 'object') { + return {}; + } + + if (Array.isArray(splunkObject)) { + return splunkObject.reduce((acc, cur) => { + acc = { ...acc, ...compactEntryContent(cur) }; + return acc; + }, {}); + } + + if (splunkObject['s:dict']) { + const obj = splunkObject['s:dict']['s:key']; + return { [splunkObject.$.name]: compactEntryContent(obj) }; + } + + if (splunkObject['s:list']) { + const items = splunkObject['s:list']['s:item']; + return { [splunkObject.$.name]: items }; + } + + if (splunkObject._) { + return { + [splunkObject.$.name]: splunkObject._, + }; + } + + return { + [splunkObject.$.name]: '', + }; +} + +function formatEntryContent(content: any): any { + return content['s:dict']['s:key'].reduce((acc: any, cur: any) => { + acc = { ...acc, ...compactEntryContent(cur) }; + return acc; + }, {}); +} + +function formatEntry(entry: any): any { + const { content, link, ...rest } = entry; + const formattedEntry = { ...rest, ...formatEntryContent(content) }; + + if (formattedEntry.id) { + formattedEntry.entryUrl = formattedEntry.id; + formattedEntry.id = formattedEntry.id.split('/').pop(); + } + + return formattedEntry; +} + +// ---------------------------------------- +// search formatting +// ---------------------------------------- + +export function formatSearch(responseData: SplunkSearchResponse) { + const { entry: entries } = responseData; + + if (!entries) return []; + + return Array.isArray(entries) ? entries.map(formatEntry) : [formatEntry(entries)]; +} + +// ---------------------------------------- +// utils +// ---------------------------------------- + +export async function parseXml(xml: string) { + return new Promise((resolve, reject) => { + parseString(xml, { explicitArray: false }, (error, result) => { + error ? reject(error) : resolve(result); + }); + }); +} + +export function extractErrorDescription(rawError: SplunkError) { + const messages = rawError.response?.messages; + return messages ? { [messages.msg.$.type.toLowerCase()]: messages.msg._ } : rawError; +} + +export function toUnixEpoch(timestamp: string) { + return Date.parse(timestamp) / 1000; +} + export async function splunkApiRequest( this: IExecuteFunctions | ILoadOptionsFunctions, method: string, @@ -65,39 +153,6 @@ export async function splunkApiRequest( } } -// ---------------------------------------- -// utils -// ---------------------------------------- - -export async function parseXml(xml: string) { - return new Promise((resolve, reject) => { - parseString(xml, { explicitArray: false }, (error, result) => { - error ? reject(error) : resolve(result); - }); - }); -} - -export function extractErrorDescription(rawError: SplunkError) { - const messages = rawError.response?.messages; - return messages ? { [messages.msg.$.type.toLowerCase()]: messages.msg._ } : rawError; -} - -export function toUnixEpoch(timestamp: string) { - return Date.parse(timestamp) / 1000; -} - -// ---------------------------------------- -// search formatting -// ---------------------------------------- - -export function formatSearch(responseData: SplunkSearchResponse) { - const { entry: entries } = responseData; - - if (!entries) return []; - - return Array.isArray(entries) ? entries.map(formatEntry) : [formatEntry(entries)]; -} - // ---------------------------------------- // feed formatting // ---------------------------------------- @@ -113,25 +168,6 @@ export function formatFeed(responseData: SplunkFeedResponse) { // ---------------------------------------- // result formatting // ---------------------------------------- - -export function formatResults(responseData: SplunkResultResponse) { - const results = responseData.results.result; - if (!results) return []; - - return Array.isArray(results) - ? results.map((r) => formatResult(r.field)) - : [formatResult(results.field)]; -} - -/* tslint:disable: no-any */ - -function formatResult(field: any): any { - return field.reduce((acc: any, cur: any) => { - acc = { ...acc, ...compactResult(cur) }; - return acc; - }, {}); -} - function compactResult(splunkObject: any): any { if (typeof splunkObject !== 'object') { return {}; @@ -152,60 +188,20 @@ function compactResult(splunkObject: any): any { }; } -// ---------------------------------------- -// entry formatting -// ---------------------------------------- - -function formatEntry(entry: any): any { - const { content, link, ...rest } = entry; - const formattedEntry = { ...rest, ...formatEntryContent(content) }; - - if (formattedEntry.id) { - formattedEntry.entryUrl = formattedEntry.id; - formattedEntry.id = formattedEntry.id.split('/').pop(); - } - - return formattedEntry; -} - -function formatEntryContent(content: any): any { - return content['s:dict']['s:key'].reduce((acc: any, cur: any) => { - acc = { ...acc, ...compactEntryContent(cur) }; +function formatResult(field: any): any { + return field.reduce((acc: any, cur: any) => { + acc = { ...acc, ...compactResult(cur) }; return acc; }, {}); } -function compactEntryContent(splunkObject: any): any { - if (typeof splunkObject !== 'object') { - return {}; - } +export function formatResults(responseData: SplunkResultResponse) { + const results = responseData.results.result; + if (!results) return []; - if (Array.isArray(splunkObject)) { - return splunkObject.reduce((acc, cur) => { - acc = { ...acc, ...compactEntryContent(cur) }; - return acc; - }, {}); - } - - if (splunkObject['s:dict']) { - const obj = splunkObject['s:dict']['s:key']; - return { [splunkObject.$.name]: compactEntryContent(obj) }; - } - - if (splunkObject['s:list']) { - const items = splunkObject['s:list']['s:item']; - return { [splunkObject.$.name]: items }; - } - - if (splunkObject._) { - return { - [splunkObject.$.name]: splunkObject._, - }; - } - - return { - [splunkObject.$.name]: '', - }; + return Array.isArray(results) + ? results.map((r) => formatResult(r.field)) + : [formatResult(results.field)]; } // ---------------------------------------- diff --git a/packages/nodes-base/nodes/Spotify/GenericFunctions.ts b/packages/nodes-base/nodes/Spotify/GenericFunctions.ts index 97e8496696..34af2ce7fc 100644 --- a/packages/nodes-base/nodes/Spotify/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Spotify/GenericFunctions.ts @@ -26,7 +26,7 @@ export async function spotifyApiRequest( Accept: ' application/json', }, qs: query, - uri: uri || `https://api.spotify.com/v1${endpoint}`, + uri: uri ?? `https://api.spotify.com/v1${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/Ssh/Ssh.node.ts b/packages/nodes-base/nodes/Ssh/Ssh.node.ts index 372a122251..dab659a770 100644 --- a/packages/nodes-base/nodes/Ssh/Ssh.node.ts +++ b/packages/nodes-base/nodes/Ssh/Ssh.node.ts @@ -1,6 +1,7 @@ import { IExecuteFunctions } from 'n8n-core'; import { + IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, @@ -11,7 +12,7 @@ import { readFile, rm, writeFile } from 'fs/promises'; import { file } from 'tmp-promise'; -const nodeSSH = require('node-ssh'); +import { NodeSSH } from 'node-ssh'; export class Ssh implements INodeType { description: INodeTypeDescription = { @@ -252,7 +253,7 @@ export class Ssh implements INodeType { const temporaryFiles: string[] = []; - const ssh = new nodeSSH.NodeSSH(); + const ssh = new NodeSSH(); try { if (authentication === 'password') { @@ -292,7 +293,7 @@ export class Ssh implements INodeType { const command = this.getNodeParameter('command', i) as string; const cwd = this.getNodeParameter('cwd', i) as string; returnItems.push({ - json: await ssh.execCommand(command, { cwd }), + json: (await ssh.execCommand(command, { cwd })) as unknown as IDataObject, pairedItem: { item: i, }, diff --git a/packages/nodes-base/nodes/Stackby/GenericFunction.ts b/packages/nodes-base/nodes/Stackby/GenericFunction.ts index ebfa42636e..fcdf29fd32 100644 --- a/packages/nodes-base/nodes/Stackby/GenericFunction.ts +++ b/packages/nodes-base/nodes/Stackby/GenericFunction.ts @@ -27,7 +27,7 @@ export async function apiRequest( method, body, qs: query, - uri: uri || `https://stackby.com/api/betav1${endpoint}`, + uri: uri ?? `https://stackby.com/api/betav1${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/Strava/GenericFunctions.ts b/packages/nodes-base/nodes/Strava/GenericFunctions.ts index f43ceb272e..4c213202e7 100644 --- a/packages/nodes-base/nodes/Strava/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Strava/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function stravaApiRequest( method, form: body, qs, - uri: uri || `https://www.strava.com/api/v3${resource}`, + uri: uri ?? `https://www.strava.com/api/v3${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Stripe/helpers.ts b/packages/nodes-base/nodes/Stripe/helpers.ts index e1a9af4114..8360a3dd32 100644 --- a/packages/nodes-base/nodes/Stripe/helpers.ts +++ b/packages/nodes-base/nodes/Stripe/helpers.ts @@ -30,16 +30,6 @@ export async function stripeApiRequest( return this.helpers.requestWithAuthentication.call(this, 'stripeApi', options); } -/** - * Make n8n's charge fields compliant with the Stripe API request object. - */ -export const adjustChargeFields = flow([adjustShipping, adjustMetadata]); - -/** - * Make n8n's customer fields compliant with the Stripe API request object. - */ -export const adjustCustomerFields = flow([adjustShipping, adjustAddress, adjustMetadata]); - /** * Convert n8n's address object into a Stripe API request shipping object. */ @@ -91,6 +81,16 @@ function adjustShipping(shippingFields: { }; } +/** + * Make n8n's charge fields compliant with the Stripe API request object. + */ +export const adjustChargeFields = flow([adjustShipping, adjustMetadata]); + +/** + * Make n8n's customer fields compliant with the Stripe API request object. + */ +export const adjustCustomerFields = flow([adjustShipping, adjustAddress, adjustMetadata]); + /** * Load a resource so it can be selected by name from a dropdown. */ diff --git a/packages/nodes-base/nodes/Supabase/GenericFunctions.ts b/packages/nodes-base/nodes/Supabase/GenericFunctions.ts index c7744d2843..64f4c2f99c 100644 --- a/packages/nodes-base/nodes/Supabase/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Supabase/GenericFunctions.ts @@ -43,7 +43,7 @@ export async function supabaseApiRequest( method, qs, body, - uri: uri || `${credentials.host}/rest/v1${resource}`, + uri: uri ?? `${credentials.host}/rest/v1${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/SurveyMonkey/GenericFunctions.ts b/packages/nodes-base/nodes/SurveyMonkey/GenericFunctions.ts index 6c9cea0cc0..095d75b90f 100644 --- a/packages/nodes-base/nodes/SurveyMonkey/GenericFunctions.ts +++ b/packages/nodes-base/nodes/SurveyMonkey/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function surveyMonkeyApiRequest( method, body, qs: query, - uri: uri || `${endpoint}${resource}`, + uri: uri ?? `${endpoint}${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts b/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts index 5d3ca8cd7a..d8843377d2 100644 --- a/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts +++ b/packages/nodes-base/nodes/SurveyMonkey/SurveyMonkeyTrigger.node.ts @@ -691,7 +691,7 @@ export class SurveyMonkeyTrigger implements INodeType { } const addressInfo: IDataObject = {}; for (const answer of question.answers.rows as IDataObject[]) { - addressInfo[answer.type as string] = rows[answer.id as string] || ''; + addressInfo[answer.type as string] = rows[answer.id as string] ?? ''; } responseQuestions.set(heading, addressInfo); } diff --git a/packages/nodes-base/nodes/Switch/Switch.node.ts b/packages/nodes-base/nodes/Switch/Switch.node.ts index 9d29647adc..65e55ae16f 100644 --- a/packages/nodes-base/nodes/Switch/Switch.node.ts +++ b/packages/nodes-base/nodes/Switch/Switch.node.ts @@ -532,13 +532,13 @@ export class Switch implements INodeType { [key: string]: (value1: NodeParameterValue, value2: NodeParameterValue) => boolean; } = { after: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) > (value2 || 0), + (value1 ?? 0) > (value2 ?? 0), before: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) < (value2 || 0), + (value1 ?? 0) < (value2 ?? 0), contains: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || '').toString().includes((value2 || '').toString()), + (value1 ?? '').toString().includes((value2 ?? '').toString()), notContains: (value1: NodeParameterValue, value2: NodeParameterValue) => - !(value1 || '').toString().includes((value2 || '').toString()), + !(value1 ?? '').toString().includes((value2 ?? '').toString()), endsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).endsWith(value2 as string), notEndsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => @@ -546,44 +546,44 @@ export class Switch implements INodeType { equal: (value1: NodeParameterValue, value2: NodeParameterValue) => value1 === value2, notEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => value1 !== value2, larger: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) > (value2 || 0), + (value1 ?? 0) > (value2 ?? 0), largerEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) >= (value2 || 0), + (value1 ?? 0) >= (value2 ?? 0), smaller: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) < (value2 || 0), + (value1 ?? 0) < (value2 ?? 0), smallerEqual: (value1: NodeParameterValue, value2: NodeParameterValue) => - (value1 || 0) <= (value2 || 0), + (value1 ?? 0) <= (value2 ?? 0), startsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => (value1 as string).startsWith(value2 as string), notStartsWith: (value1: NodeParameterValue, value2: NodeParameterValue) => !(value1 as string).startsWith(value2 as string), regex: (value1: NodeParameterValue, value2: NodeParameterValue) => { - const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$')); + const regexMatch = (value2 ?? '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$')); let regex: RegExp; if (!regexMatch) { - regex = new RegExp((value2 || '').toString()); + regex = new RegExp((value2 ?? '').toString()); } else if (regexMatch.length === 1) { regex = new RegExp(regexMatch[1]); } else { regex = new RegExp(regexMatch[1], regexMatch[2]); } - return !!(value1 || '').toString().match(regex); + return !!(value1 ?? '').toString().match(regex); }, notRegex: (value1: NodeParameterValue, value2: NodeParameterValue) => { - const regexMatch = (value2 || '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$')); + const regexMatch = (value2 ?? '').toString().match(new RegExp('^/(.*?)/([gimusy]*)$')); let regex: RegExp; if (!regexMatch) { - regex = new RegExp((value2 || '').toString()); + regex = new RegExp((value2 ?? '').toString()); } else if (regexMatch.length === 1) { regex = new RegExp(regexMatch[1]); } else { regex = new RegExp(regexMatch[1], regexMatch[2]); } - return !(value1 || '').toString().match(regex); + return !(value1 ?? '').toString().match(regex); }, }; diff --git a/packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts b/packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts index c22ca307af..446286d34b 100644 --- a/packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Tapfiliate/GenericFunctions.ts @@ -28,7 +28,7 @@ export async function tapfiliateApiRequest( method, qs, body, - uri: uri || `https://api.tapfiliate.com/1.6${endpoint}`, + uri: uri ?? `https://api.tapfiliate.com/1.6${endpoint}`, json: true, }; diff --git a/packages/nodes-base/nodes/Telegram/GenericFunctions.ts b/packages/nodes-base/nodes/Telegram/GenericFunctions.ts index 10f81f2fee..fb77d3f500 100644 --- a/packages/nodes-base/nodes/Telegram/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Telegram/GenericFunctions.ts @@ -149,7 +149,7 @@ export async function apiRequest( ): Promise { const credentials = await this.getCredentials('telegramApi'); - query = query || {}; + query = query ?? {}; const options: OptionsWithUri = { headers: {}, diff --git a/packages/nodes-base/nodes/Telegram/Telegram.node.ts b/packages/nodes-base/nodes/Telegram/Telegram.node.ts index fb920749dd..cc3ed94e28 100644 --- a/packages/nodes-base/nodes/Telegram/Telegram.node.ts +++ b/packages/nodes-base/nodes/Telegram/Telegram.node.ts @@ -1977,7 +1977,7 @@ export class Telegram implements INodeType { ); } - body.disable_notification = body.disable_notification?.toString() || 'false'; + body.disable_notification = body.disable_notification?.toString() ?? 'false'; const formData = { ...body, diff --git a/packages/nodes-base/nodes/TheHive/GenericFunctions.ts b/packages/nodes-base/nodes/TheHive/GenericFunctions.ts index ea7e78ab7d..a5039d7bc2 100644 --- a/packages/nodes-base/nodes/TheHive/GenericFunctions.ts +++ b/packages/nodes-base/nodes/TheHive/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function theHiveApiRequest( let options: OptionsWithUri = { method, qs: query, - uri: uri || `${credentials.url}/api${resource}`, + uri: uri ?? `${credentials.url}/api${resource}`, body, rejectUnauthorized: !credentials.allowUnauthorizedCerts, json: true, diff --git a/packages/nodes-base/nodes/Todoist/v1/OperationHandler.ts b/packages/nodes-base/nodes/Todoist/v1/OperationHandler.ts index 729927ed0e..54e18af93d 100644 --- a/packages/nodes-base/nodes/Todoist/v1/OperationHandler.ts +++ b/packages/nodes-base/nodes/Todoist/v1/OperationHandler.ts @@ -12,6 +12,57 @@ export interface OperationHandler { handleOperation(ctx: Context, itemIndex: number): Promise; } +export interface CreateTaskRequest { + content?: string; + description?: string; + project_id?: number; + section_id?: number; + parent_id?: string; + order?: number; + labels?: string[]; + priority?: number; + due_string?: string; + due_datetime?: string; + due_date?: string; + due_lang?: string; +} + +export interface SyncRequest { + commands: Command[]; + temp_id_mapping?: IDataObject; +} + +export interface Command { + type: CommandType; + uuid: string; + temp_id?: string; + args: { + id?: number; + section_id?: number; + project_id?: number | string; + section?: string; + content?: string; + }; +} + +export enum CommandType { + ITEM_MOVE = 'item_move', + ITEM_ADD = 'item_add', + ITEM_UPDATE = 'item_update', + ITEM_REORDER = 'item_reorder', + ITEM_DELETE = 'item_delete', + ITEM_COMPLETE = 'item_complete', +} + +async function getLabelNameFromId(ctx: Context, labelIds: number[]): Promise { + const labelList = []; + for (const label of labelIds) { + const thisLabel = await todoistApiRequest.call(ctx, 'GET', `/labels/${label}`); + labelList.push(thisLabel.name); + } + return labelList; +} + export class CreateHandler implements OperationHandler { async handleOperation(ctx: Context, itemIndex: number): Promise { //https://developer.todoist.com/rest/v2/#create-a-new-task @@ -301,54 +352,3 @@ export class SyncHandler implements OperationHandler { return command.type === CommandType.ITEM_ADD; } } - -async function getLabelNameFromId(ctx: Context, labelIds: number[]): Promise { - const labelList = []; - for (const label of labelIds) { - const thisLabel = await todoistApiRequest.call(ctx, 'GET', `/labels/${label}`); - labelList.push(thisLabel.name); - } - return labelList; -} - -export interface CreateTaskRequest { - content?: string; - description?: string; - project_id?: number; - section_id?: number; - parent_id?: string; - order?: number; - labels?: string[]; - priority?: number; - due_string?: string; - due_datetime?: string; - due_date?: string; - due_lang?: string; -} - -export interface SyncRequest { - commands: Command[]; - temp_id_mapping?: IDataObject; -} - -export interface Command { - type: CommandType; - uuid: string; - temp_id?: string; - args: { - id?: number; - section_id?: number; - project_id?: number | string; - section?: string; - content?: string; - }; -} - -export enum CommandType { - ITEM_MOVE = 'item_move', - ITEM_ADD = 'item_add', - ITEM_UPDATE = 'item_update', - ITEM_REORDER = 'item_reorder', - ITEM_DELETE = 'item_delete', - ITEM_COMPLETE = 'item_complete', -} diff --git a/packages/nodes-base/nodes/Todoist/v2/OperationHandler.ts b/packages/nodes-base/nodes/Todoist/v2/OperationHandler.ts index 179d615821..c8d8a09c97 100644 --- a/packages/nodes-base/nodes/Todoist/v2/OperationHandler.ts +++ b/packages/nodes-base/nodes/Todoist/v2/OperationHandler.ts @@ -12,6 +12,48 @@ export interface OperationHandler { handleOperation(ctx: Context, itemIndex: number): Promise; } +export interface CreateTaskRequest { + content?: string; + description?: string; + project_id?: number; + section_id?: number; + parent_id?: string; + order?: number; + labels?: string[]; + priority?: number; + due_string?: string; + due_datetime?: string; + due_date?: string; + due_lang?: string; +} + +export interface SyncRequest { + commands: Command[]; + temp_id_mapping?: IDataObject; +} + +export interface Command { + type: CommandType; + uuid: string; + temp_id?: string; + args: { + id?: number; + section_id?: number; + project_id?: number | string; + section?: string; + content?: string; + }; +} + +export enum CommandType { + ITEM_MOVE = 'item_move', + ITEM_ADD = 'item_add', + ITEM_UPDATE = 'item_update', + ITEM_REORDER = 'item_reorder', + ITEM_DELETE = 'item_delete', + ITEM_COMPLETE = 'item_complete', +} + export class CreateHandler implements OperationHandler { async handleOperation(ctx: Context, itemIndex: number): Promise { //https://developer.todoist.com/rest/v2/#create-a-new-task @@ -301,45 +343,3 @@ export class SyncHandler implements OperationHandler { return command.type === CommandType.ITEM_ADD; } } - -export interface CreateTaskRequest { - content?: string; - description?: string; - project_id?: number; - section_id?: number; - parent_id?: string; - order?: number; - labels?: string[]; - priority?: number; - due_string?: string; - due_datetime?: string; - due_date?: string; - due_lang?: string; -} - -export interface SyncRequest { - commands: Command[]; - temp_id_mapping?: IDataObject; -} - -export interface Command { - type: CommandType; - uuid: string; - temp_id?: string; - args: { - id?: number; - section_id?: number; - project_id?: number | string; - section?: string; - content?: string; - }; -} - -export enum CommandType { - ITEM_MOVE = 'item_move', - ITEM_ADD = 'item_add', - ITEM_UPDATE = 'item_update', - ITEM_REORDER = 'item_reorder', - ITEM_DELETE = 'item_delete', - ITEM_COMPLETE = 'item_complete', -} diff --git a/packages/nodes-base/nodes/Toggl/GenericFunctions.ts b/packages/nodes-base/nodes/Toggl/GenericFunctions.ts index 20dd6800ab..f31f49d67a 100644 --- a/packages/nodes-base/nodes/Toggl/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Toggl/GenericFunctions.ts @@ -40,7 +40,7 @@ export async function togglApiRequest( headers: headerWithAuthentication, method, qs: query, - uri: uri || `https://api.track.toggl.com/api/v8${resource}`, + uri: uri ?? `https://api.track.toggl.com/api/v8${resource}`, body, json: true, }; diff --git a/packages/nodes-base/nodes/TravisCi/GenericFunctions.ts b/packages/nodes-base/nodes/TravisCi/GenericFunctions.ts index 64559d1293..9df57aec04 100644 --- a/packages/nodes-base/nodes/TravisCi/GenericFunctions.ts +++ b/packages/nodes-base/nodes/TravisCi/GenericFunctions.ts @@ -32,7 +32,7 @@ export async function travisciApiRequest( method, qs, body, - uri: uri || `https://api.travis-ci.com${resource}`, + uri: uri ?? `https://api.travis-ci.com${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Trello/GenericFunctions.ts b/packages/nodes-base/nodes/Trello/GenericFunctions.ts index 01604c3457..5dfda99a9f 100644 --- a/packages/nodes-base/nodes/Trello/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Trello/GenericFunctions.ts @@ -15,7 +15,7 @@ export async function apiRequest( body: object, query?: IDataObject, ): Promise { - query = query || {}; + query = query ?? {}; const options: OptionsWithUri = { method, diff --git a/packages/nodes-base/nodes/Twake/GenericFunctions.ts b/packages/nodes-base/nodes/Twake/GenericFunctions.ts index 966abe5234..a98aaf46ff 100644 --- a/packages/nodes-base/nodes/Twake/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Twake/GenericFunctions.ts @@ -19,7 +19,7 @@ export async function twakeApiRequest( method, body, qs: query, - uri: uri || `https://plugins.twake.app/plugins/n8n${resource}`, + uri: uri ?? `https://plugins.twake.app/plugins/n8n${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Twitter/GenericFunctions.ts b/packages/nodes-base/nodes/Twitter/GenericFunctions.ts index 6efa943dd8..9aabeebee6 100644 --- a/packages/nodes-base/nodes/Twitter/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Twitter/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function twitterApiRequest( method, body, qs, - url: uri || `https://api.twitter.com/1.1${resource}`, + url: uri ?? `https://api.twitter.com/1.1${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Twitter/Twitter.node.ts b/packages/nodes-base/nodes/Twitter/Twitter.node.ts index 42157970f6..50f4e998f9 100644 --- a/packages/nodes-base/nodes/Twitter/Twitter.node.ts +++ b/packages/nodes-base/nodes/Twitter/Twitter.node.ts @@ -21,7 +21,7 @@ import { import { ITweet } from './TweetInterface'; -const ISO6391 = require('iso-639-1'); +import ISO6391 from 'iso-639-1'; export class Twitter implements INodeType { description: INodeTypeDescription = { @@ -247,7 +247,7 @@ export class Twitter implements INodeType { } } - qs.tweet_mode = additionalFields.tweetMode || 'compat'; + qs.tweet_mode = additionalFields.tweetMode ?? 'compat'; if (returnAll) { responseData = await twitterApiRequestAllItems.call( diff --git a/packages/nodes-base/nodes/Typeform/GenericFunctions.ts b/packages/nodes-base/nodes/Typeform/GenericFunctions.ts index eed52bcb68..c18cc5145f 100644 --- a/packages/nodes-base/nodes/Typeform/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Typeform/GenericFunctions.ts @@ -49,7 +49,7 @@ export async function apiRequest( json: true, }; - query = query || {}; + query = query ?? {}; try { if (authenticationMethod === 'accessToken') { diff --git a/packages/nodes-base/nodes/UProc/GroupDescription.ts b/packages/nodes-base/nodes/UProc/GroupDescription.ts index 0619020e82..0a5ae8924e 100644 --- a/packages/nodes-base/nodes/UProc/GroupDescription.ts +++ b/packages/nodes-base/nodes/UProc/GroupDescription.ts @@ -16,7 +16,10 @@ for (const group of (groups as IDataObject).groups as IDataObject[]) { const item = { name: group.translated, value: group.name, - description: 'The ' + group.translated + ' Resource allows you to get tools from this resource', + description: + 'The ' + + (group.translated as string) + + ' Resource allows you to get tools from this resource', }; options.push(item); } diff --git a/packages/nodes-base/nodes/Uplead/GenericFunctions.ts b/packages/nodes-base/nodes/Uplead/GenericFunctions.ts index 073bdaec2e..115015094b 100644 --- a/packages/nodes-base/nodes/Uplead/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Uplead/GenericFunctions.ts @@ -23,7 +23,7 @@ export async function upleadApiRequest( method, qs, body, - uri: uri || `https://api.uplead.com/v2${resource}`, + uri: uri ?? `https://api.uplead.com/v2${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/UptimeRobot/GenericFunctions.ts b/packages/nodes-base/nodes/UptimeRobot/GenericFunctions.ts index dfc97e096a..0979c0fcef 100644 --- a/packages/nodes-base/nodes/UptimeRobot/GenericFunctions.ts +++ b/packages/nodes-base/nodes/UptimeRobot/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function uptimeRobotApiRequest( api_key: credentials.apiKey, ...body, }, - uri: uri || `https://api.uptimerobot.com/v2${resource}`, + uri: uri ?? `https://api.uptimerobot.com/v2${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Venafi/Datacenter/GenericFunctions.ts b/packages/nodes-base/nodes/Venafi/Datacenter/GenericFunctions.ts index fbc6e18439..04d7838f3a 100644 --- a/packages/nodes-base/nodes/Venafi/Datacenter/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Venafi/Datacenter/GenericFunctions.ts @@ -30,7 +30,7 @@ export async function venafiApiRequest( body, qs, rejectUnauthorized: !credentials.allowUnauthorizedCerts, - uri: uri || `${credentials.domain}${resource}`, + uri: uri ?? `${credentials.domain}${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenter.node.ts b/packages/nodes-base/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenter.node.ts index 9e549f1aa9..b4a2fc022b 100644 --- a/packages/nodes-base/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenter.node.ts +++ b/packages/nodes-base/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenter.node.ts @@ -119,7 +119,8 @@ export class VenafiTlsProtectDatacenter implements INodeType { if (includePrivateKey) { const password = this.getNodeParameter('password', i) as string; - (body.IncludePrivateKey = true), (body.Password = password); + body.IncludePrivateKey = true; + body.Password = password; } Object.assign(body, additionalFields); diff --git a/packages/nodes-base/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenterTrigger.node.ts b/packages/nodes-base/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenterTrigger.node.ts index 0f66636db7..24320a6914 100644 --- a/packages/nodes-base/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenterTrigger.node.ts +++ b/packages/nodes-base/nodes/Venafi/Datacenter/VenafiTlsProtectDatacenterTrigger.node.ts @@ -51,7 +51,7 @@ export class VenafiTlsProtectDatacenterTrigger implements INodeType { const now = moment().format(); - qs.ValidToGreater = webhookData.lastTimeChecked || now; + qs.ValidToGreater = webhookData.lastTimeChecked ?? now; qs.ValidToLess = now; diff --git a/packages/nodes-base/nodes/Vero/GenericFunctions.ts b/packages/nodes-base/nodes/Vero/GenericFunctions.ts index 2bf80d7b93..83617d6b09 100644 --- a/packages/nodes-base/nodes/Vero/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Vero/GenericFunctions.ts @@ -22,7 +22,7 @@ export async function veroApiRequest( auth_token: credentials.authToken, ...body, }, - uri: uri || `https://api.getvero.com/api/v2${resource}`, + uri: uri ?? `https://api.getvero.com/api/v2${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Wait/Wait.node.ts b/packages/nodes-base/nodes/Wait/Wait.node.ts index b164ff9b0e..4efa81643a 100644 --- a/packages/nodes-base/nodes/Wait/Wait.node.ts +++ b/packages/nodes-base/nodes/Wait/Wait.node.ts @@ -717,7 +717,7 @@ export class Wait implements INodeType { const fileJson = file.toJSON(); returnItem.binary![binaryPropertyName] = await this.helpers.copyBinaryFile( file.path, - fileJson.name || fileJson.filename, + fileJson.name ?? fileJson.filename, fileJson.type as string, ); @@ -747,7 +747,7 @@ export class Wait implements INodeType { }, }; - const binaryPropertyName = (options.binaryPropertyName || 'data') as string; + const binaryPropertyName = (options.binaryPropertyName ?? 'data') as string; returnItem.binary![binaryPropertyName] = await this.helpers.copyBinaryFile( binaryFile.path, mimeType, diff --git a/packages/nodes-base/nodes/Webflow/GenericFunctions.ts b/packages/nodes-base/nodes/Webflow/GenericFunctions.ts index e0699f4a6e..72c5e15ae1 100644 --- a/packages/nodes-base/nodes/Webflow/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Webflow/GenericFunctions.ts @@ -36,7 +36,7 @@ export async function webflowApiRequest( method, qs, body, - uri: uri || `https://api.webflow.com${resource}`, + uri: uri ?? `https://api.webflow.com${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Webhook/Webhook.node.ts b/packages/nodes-base/nodes/Webhook/Webhook.node.ts index 2e9d4d2134..1975a7c4c4 100644 --- a/packages/nodes-base/nodes/Webhook/Webhook.node.ts +++ b/packages/nodes-base/nodes/Webhook/Webhook.node.ts @@ -529,7 +529,7 @@ export class Webhook implements INodeType { const fileJson = file.toJSON(); returnItem.binary![binaryPropertyName] = await this.helpers.copyBinaryFile( file.path, - fileJson.name || fileJson.filename, + fileJson.name ?? fileJson.filename, fileJson.type as string, ); @@ -559,7 +559,7 @@ export class Webhook implements INodeType { }, }; - const binaryPropertyName = (options.binaryPropertyName || 'data') as string; + const binaryPropertyName = (options.binaryPropertyName ?? 'data') as string; returnItem.binary![binaryPropertyName] = await this.helpers.copyBinaryFile( binaryFile.path, mimeType, diff --git a/packages/nodes-base/nodes/Wekan/GenericFunctions.ts b/packages/nodes-base/nodes/Wekan/GenericFunctions.ts index a96bc99153..cea58a3930 100644 --- a/packages/nodes-base/nodes/Wekan/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Wekan/GenericFunctions.ts @@ -13,7 +13,7 @@ export async function apiRequest( ): Promise { const credentials = await this.getCredentials('wekanApi'); - query = query || {}; + query = query ?? {}; const options: OptionsWithUri = { headers: { diff --git a/packages/nodes-base/nodes/WhatsApp/MediaFunctions.ts b/packages/nodes-base/nodes/WhatsApp/MediaFunctions.ts index 1e3d8a32ac..6742da52e8 100644 --- a/packages/nodes-base/nodes/WhatsApp/MediaFunctions.ts +++ b/packages/nodes-base/nodes/WhatsApp/MediaFunctions.ts @@ -35,7 +35,7 @@ export async function setupUpload( const data = new FormData(); data.append('file', buffer, { contentType: mimeType, - filename: mediaFileName || binaryFileName, + filename: mediaFileName ?? binaryFileName, }); data.append('messaging_product', 'whatsapp'); diff --git a/packages/nodes-base/nodes/WhatsApp/MessageFunctions.ts b/packages/nodes-base/nodes/WhatsApp/MessageFunctions.ts index 98508b8772..fa2cdb4b6d 100644 --- a/packages/nodes-base/nodes/WhatsApp/MessageFunctions.ts +++ b/packages/nodes-base/nodes/WhatsApp/MessageFunctions.ts @@ -83,7 +83,7 @@ export async function mediaUploadFromItem( const data = new FormData(); data.append('file', await BinaryDataManager.getInstance().retrieveBinaryData(binaryFile), { contentType: mimeType, - filename: mediaFileName || binaryFileName, + filename: mediaFileName ?? binaryFileName, }); data.append('messaging_product', 'whatsapp'); @@ -105,7 +105,7 @@ export async function mediaUploadFromItem( set( requestOptions.body as IDataObject, `${operation}.filename`, - mediaFileName || binaryFileName, + mediaFileName ?? binaryFileName, ); } diff --git a/packages/nodes-base/nodes/WhatsApp/MessagesDescription.ts b/packages/nodes-base/nodes/WhatsApp/MessagesDescription.ts index 8f05d60475..4fbd9a5a40 100644 --- a/packages/nodes-base/nodes/WhatsApp/MessagesDescription.ts +++ b/packages/nodes-base/nodes/WhatsApp/MessagesDescription.ts @@ -1,3 +1,4 @@ +import countryCodes from 'currency-codes'; import { INodeProperties } from 'n8n-workflow'; import { cleanPhoneNumber, @@ -10,11 +11,12 @@ import { export const mediaTypes = ['image', 'video', 'audio', 'sticker', 'document']; -let currencies = require('currency-codes/data'); -currencies = currencies.map(({ code, currency }: { code: string; currency: string }) => ({ - name: `${code} - ${currency}`, - value: code, -})); +const currencies = countryCodes.data.map( + ({ code, currency }: { code: string; currency: string }) => ({ + name: `${code} - ${currency}`, + value: code, + }), +); export const messageFields: INodeProperties[] = [ { diff --git a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts index fa4b499bde..bd274dfad3 100644 --- a/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts +++ b/packages/nodes-base/nodes/WooCommerce/GenericFunctions.ts @@ -39,7 +39,7 @@ export async function woocommerceApiRequest( method, qs, body, - uri: uri || `${credentials.url}/wp-json/wc/v3${resource}`, + uri: uri ?? `${credentials.url}/wp-json/wc/v3${resource}`, json: true, }; diff --git a/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts b/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts index bfcf68b0da..801421ff47 100644 --- a/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Wordpress/GenericFunctions.ts @@ -25,7 +25,7 @@ export async function wordpressApiRequest( method, qs, body, - uri: uri || `${credentials.url}/wp-json/wp/v2${resource}`, + uri: uri ?? `${credentials.url}/wp-json/wp/v2${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/Workable/GenericFunctions.ts b/packages/nodes-base/nodes/Workable/GenericFunctions.ts index baa63e27eb..289b7e2b85 100644 --- a/packages/nodes-base/nodes/Workable/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Workable/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function workableApiRequest( method, qs, body, - uri: uri || `https://${credentials.subdomain}.workable.com/spi/v3${resource}`, + uri: uri ?? `https://${credentials.subdomain}.workable.com/spi/v3${resource}`, json: true, }; options = Object.assign({}, options, option); diff --git a/packages/nodes-base/nodes/WorkflowTrigger/WorkflowTrigger.node.ts b/packages/nodes-base/nodes/WorkflowTrigger/WorkflowTrigger.node.ts index c9a0a0edb7..64a2e851c0 100644 --- a/packages/nodes-base/nodes/WorkflowTrigger/WorkflowTrigger.node.ts +++ b/packages/nodes-base/nodes/WorkflowTrigger/WorkflowTrigger.node.ts @@ -69,18 +69,17 @@ export class WorkflowTrigger implements INodeType { ]); } - const self = this; - async function manualTriggerFunction() { - self.emit([ - self.helpers.returnJsonArray([ + const manualTriggerFunction = async () => { + this.emit([ + this.helpers.returnJsonArray([ { event: 'Manual execution', timestamp: new Date().toISOString(), - workflow_id: self.getWorkflow().id, + workflow_id: this.getWorkflow().id, }, ]), ]); - } + }; return { manualTriggerFunction, diff --git a/packages/nodes-base/nodes/Xero/GenericFunctions.ts b/packages/nodes-base/nodes/Xero/GenericFunctions.ts index cb99717f43..dfe634aab5 100644 --- a/packages/nodes-base/nodes/Xero/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Xero/GenericFunctions.ts @@ -21,7 +21,7 @@ export async function xeroApiRequest( method, body, qs, - uri: uri || `https://api.xero.com/api.xro/2.0${resource}`, + uri: uri ?? `https://api.xero.com/api.xro/2.0${resource}`, json: true, }; try { diff --git a/packages/nodes-base/nodes/Zammad/GenericFunctions.ts b/packages/nodes-base/nodes/Zammad/GenericFunctions.ts index 9b2037c4f8..52753ce886 100644 --- a/packages/nodes-base/nodes/Zammad/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zammad/GenericFunctions.ts @@ -8,6 +8,10 @@ import { flow } from 'lodash'; import type { Zammad } from './types'; +export function tolerateTrailingSlash(url: string) { + return url.endsWith('/') ? url.substr(0, url.length - 1) : url; +} + export async function zammadApiRequest( this: IExecuteFunctions | ILoadOptionsFunctions, method: string, @@ -105,10 +109,6 @@ export async function zammadApiRequestAllItems( return returnData; } -export function tolerateTrailingSlash(url: string) { - return url.endsWith('/') ? url.substr(0, url.length - 1) : url; -} - export function throwOnEmptyUpdate(this: IExecuteFunctions, resource: string) { throw new NodeOperationError( this.getNode(), @@ -120,12 +120,12 @@ export function throwOnEmptyUpdate(this: IExecuteFunctions, resource: string) { // loadOptions utils // ---------------------------------- +export const prettifyDisplayName = (fieldName: string) => fieldName.replace('name', ' Name'); + export const fieldToLoadOption = (i: Zammad.Field) => { return { name: i.display ? prettifyDisplayName(i.display) : i.name, value: i.name }; }; -export const prettifyDisplayName = (fieldName: string) => fieldName.replace('name', ' Name'); - export const isCustomer = (user: Zammad.User) => user.role_ids.includes(3) && !user.email.endsWith('@zammad.org'); diff --git a/packages/nodes-base/nodes/Zammad/types.d.ts b/packages/nodes-base/nodes/Zammad/types.d.ts index e92a2ee6ff..4c11f2374b 100644 --- a/packages/nodes-base/nodes/Zammad/types.d.ts +++ b/packages/nodes-base/nodes/Zammad/types.d.ts @@ -23,9 +23,9 @@ export declare namespace Zammad { accessToken: string; }; - export type UserAdditionalFields = IDataObject & Zammad.CustomFieldsUi & Zammad.AddressUi; + export type UserAdditionalFields = IDataObject & CustomFieldsUi & AddressUi; export type UserUpdateFields = UserAdditionalFields; - export type UserFilterFields = IDataObject & Zammad.SortUi; + export type UserFilterFields = IDataObject & SortUi; export type Organization = { active: boolean; diff --git a/packages/nodes-base/nodes/Zendesk/GenericFunctions.ts b/packages/nodes-base/nodes/Zendesk/GenericFunctions.ts index 24900a39c4..44bdc423b4 100644 --- a/packages/nodes-base/nodes/Zendesk/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zendesk/GenericFunctions.ts @@ -9,6 +9,14 @@ import { import { IDataObject } from 'n8n-workflow'; +function getUri(resource: string, subdomain: string) { + if (resource.includes('webhooks')) { + return `https://${subdomain}.zendesk.com/api/v2${resource}`; + } else { + return `https://${subdomain}.zendesk.com/api/v2${resource}.json`; + } +} + export async function zendeskApiRequest( this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, @@ -33,7 +41,7 @@ export async function zendeskApiRequest( method, qs, body, - uri: uri || getUri(resource, credentials.subdomain), + uri: uri ?? getUri(resource, credentials.subdomain), json: true, qsStringifyOptions: { arrayFormat: 'brackets', @@ -90,11 +98,3 @@ export function validateJSON(json: string | undefined): any { } return result; } - -function getUri(resource: string, subdomain: string) { - if (resource.includes('webhooks')) { - return `https://${subdomain}.zendesk.com/api/v2${resource}`; - } else { - return `https://${subdomain}.zendesk.com/api/v2${resource}.json`; - } -} diff --git a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts index 4ee047d6c7..98d187b70c 100644 --- a/packages/nodes-base/nodes/Zoho/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zoho/GenericFunctions.ts @@ -22,6 +22,15 @@ import { ZohoOAuth2ApiCredentials, } from './types'; +export function throwOnErrorStatus( + this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions, + responseData: { data?: Array<{ status: string; message: string }> }, +) { + if (responseData?.data?.[0].status === 'error') { + throw new NodeOperationError(this.getNode(), responseData as Error); + } +} + export async function zohoApiRequest( this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions, method: string, @@ -133,19 +142,16 @@ export function throwOnMissingProducts( } } -export function throwOnErrorStatus( - this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions, - responseData: { data?: Array<{ status: string; message: string }> }, -) { - if (responseData?.data?.[0].status === 'error') { - throw new NodeOperationError(this.getNode(), responseData as Error); - } -} - // ---------------------------------------- // required field adjusters // ---------------------------------------- +/** + * Create a copy of an object without a specific property. + */ +const omit = (propertyToOmit: string, { [propertyToOmit]: _, ...remainingObject }) => + remainingObject; + /** * Place a product ID at a nested position in a product details field. */ @@ -317,18 +323,29 @@ export const adjustProductPayload = adjustCustomFields; // helpers // ---------------------------------------- -/** - * Create a copy of an object without a specific property. - */ -const omit = (propertyToOmit: string, { [propertyToOmit]: _, ...remainingObject }) => - remainingObject; - /** * Convert items in a Zoho CRM API response into n8n load options. */ export const toLoadOptions = (items: ResourceItems, nameProperty: NameType) => items.map((item) => ({ name: item[nameProperty], value: item.id })); +export function getModuleName(resource: string) { + const map: { [key: string]: string } = { + account: 'Accounts', + contact: 'Contacts', + deal: 'Deals', + invoice: 'Invoices', + lead: 'Leads', + product: 'Products', + purchaseOrder: 'Purchase_Orders', + salesOrder: 'Sales_Orders', + vendor: 'Vendors', + quote: 'Quotes', + }; + + return map[resource]; +} + /** * Retrieve all fields for a resource, sorted alphabetically. */ @@ -359,21 +376,13 @@ export async function getFields( return sortBy(options, (o) => o.name); } -export function getModuleName(resource: string) { - const map: { [key: string]: string } = { - account: 'Accounts', - contact: 'Contacts', - deal: 'Deals', - invoice: 'Invoices', - lead: 'Leads', - product: 'Products', - purchaseOrder: 'Purchase_Orders', - salesOrder: 'Sales_Orders', - vendor: 'Vendors', - quote: 'Quotes', - }; +export const capitalizeInitial = (str: string) => str[0].toUpperCase() + str.slice(1); - return map[resource]; +function getSectionApiName(resource: string) { + if (resource === 'purchaseOrder') return 'Purchase Order Information'; + if (resource === 'salesOrder') return 'Sales Order Information'; + + return `${capitalizeInitial(resource)} Information`; } export async function getPicklistOptions( @@ -402,13 +411,6 @@ export async function getPicklistOptions( })); } -function getSectionApiName(resource: string) { - if (resource === 'purchaseOrder') return 'Purchase Order Information'; - if (resource === 'salesOrder') return 'Sales Order Information'; - - return `${capitalizeInitial(resource)} Information`; -} - /** * Add filter options to a query string object. */ @@ -418,5 +420,3 @@ export const addGetAllFilterOptions = (qs: IDataObject, options: GetAllFilterOpt Object.assign(qs, fields && { fields: fields.join(',') }, rest); } }; - -export const capitalizeInitial = (str: string) => str[0].toUpperCase() + str.slice(1); diff --git a/packages/nodes-base/nodes/Zoom/GenericFunctions.ts b/packages/nodes-base/nodes/Zoom/GenericFunctions.ts index ffb075b22b..f253cb2b9b 100644 --- a/packages/nodes-base/nodes/Zoom/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zoom/GenericFunctions.ts @@ -17,7 +17,7 @@ export async function zoomApiRequest( let options: OptionsWithUri = { method, - headers: headers || { + headers: headers ?? { 'Content-Type': 'application/json', }, body, @@ -45,6 +45,14 @@ export async function zoomApiRequest( } } +async function wait() { + return new Promise((resolve, _reject) => { + setTimeout(() => { + resolve(true); + }, 1000); + }); +} + export async function zoomApiRequestAllItems( this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, @@ -67,10 +75,3 @@ export async function zoomApiRequestAllItems( return returnData; } -async function wait() { - return new Promise((resolve, _reject) => { - setTimeout(() => { - resolve(true); - }, 1000); - }); -} diff --git a/packages/nodes-base/nodes/Zulip/GenericFunctions.ts b/packages/nodes-base/nodes/Zulip/GenericFunctions.ts index 7c7735d716..355df5b889 100644 --- a/packages/nodes-base/nodes/Zulip/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Zulip/GenericFunctions.ts @@ -29,7 +29,7 @@ export async function zulipApiRequest( method, form: body, qs: query, - uri: uri || `${endpoint}${resource}`, + uri: uri ?? `${endpoint}${resource}`, json: true, }; if (!Object.keys(body).length) { diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 0d94ba3662..01b710c2ba 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -741,7 +741,9 @@ "@types/mailparser": "^2.7.3", "@types/mime-types": "^2.1.0", "@types/mssql": "^6.0.2", + "@types/node-ssh": "^7.0.1", "@types/nodemailer": "^6.4.0", + "@types/pdf-parse": "^1.1.1", "@types/promise-ftp": "^1.3.4", "@types/redis": "^2.8.11", "@types/request-promise-native": "~1.0.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e04a3089a9..19b0177760 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -719,7 +719,9 @@ importers: '@types/mailparser': ^2.7.3 '@types/mime-types': ^2.1.0 '@types/mssql': ^6.0.2 + '@types/node-ssh': ^7.0.1 '@types/nodemailer': ^6.4.0 + '@types/pdf-parse': ^1.1.1 '@types/promise-ftp': ^1.3.4 '@types/redis': ^2.8.11 '@types/request-promise-native': ~1.0.15 @@ -864,7 +866,9 @@ importers: '@types/mailparser': 2.7.4 '@types/mime-types': 2.1.1 '@types/mssql': 6.0.8 + '@types/node-ssh': 7.0.1 '@types/nodemailer': 6.4.6 + '@types/pdf-parse': 1.1.1 '@types/promise-ftp': 1.3.4 '@types/redis': 2.8.32 '@types/request-promise-native': 1.0.18 @@ -6041,6 +6045,14 @@ packages: '@types/node': 16.11.65 form-data: 3.0.1 + /@types/node-ssh/7.0.1: + resolution: {integrity: sha512-98EuH7UQl/WWwwDxpbANQ76HwBdzcSnC9zLSdrtVW7jjYeOTQ6TxBygbGwzZR4ho1agbd941UnHCdrXz2sS8JQ==} + dependencies: + '@types/node': 16.11.65 + '@types/ssh2': 1.11.6 + '@types/ssh2-streams': 0.1.9 + dev: true + /@types/node/14.18.33: resolution: {integrity: sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==} dev: true @@ -6097,6 +6109,10 @@ packages: '@types/express': 4.17.14 dev: true + /@types/pdf-parse/1.1.1: + resolution: {integrity: sha512-lDBKAslCwvfK2uvS1Uk+UCpGvw+JRy5vnBFANPKFSY92n/iEnunXi0KVBjPJXhsM4jtdcPnS7tuZ0zjA9x6piQ==} + dev: true + /@types/prettier/2.7.1: resolution: {integrity: sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==} dev: true @@ -6212,6 +6228,12 @@ packages: '@types/ssh2': 1.11.6 dev: true + /@types/ssh2-streams/0.1.9: + resolution: {integrity: sha512-I2J9jKqfmvXLR5GomDiCoHrEJ58hAOmFrekfFqmCFd+A6gaEStvWnPykoWUwld1PNg4G5ag1LwdA+Lz1doRJqg==} + dependencies: + '@types/node': 16.11.65 + dev: true + /@types/ssh2/1.11.6: resolution: {integrity: sha512-8Mf6bhzYYBLEB/G6COux7DS/F5bCWwojv/qFo2yH/e4cLzAavJnxvFXrYW59iKfXdhG6OmzJcXDasgOb/s0rxw==} dependencies: