mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
refactor(core): Remove linting exceptions in nodes-base (no-changelog) (#4944)
This commit is contained in:
parent
d7732ea150
commit
6608e69457
|
@ -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 }],
|
||||
},
|
||||
|
|
|
@ -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 ?? ''}`);
|
||||
|
|
|
@ -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';
|
||||
};
|
||||
|
|
|
@ -27,7 +27,7 @@ export class LemlistApi implements ICredentialType {
|
|||
credentials: ICredentialDataDecryptedObject,
|
||||
requestOptions: IHttpRequestOptions,
|
||||
): Promise<IHttpRequestOptions> {
|
||||
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;
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ export async function apiRequest(
|
|||
uri?: string,
|
||||
option: IDataObject = {},
|
||||
): Promise<any> {
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,25 +6,6 @@ import {
|
|||
INodeCredentialTestResult,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function bambooHrApiCredentialTest(
|
||||
this: ICredentialTestFunctions,
|
||||
credential: ICredentialsDecrypted,
|
||||
): Promise<INodeCredentialTestResult> {
|
||||
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<INodeCredentialTestResult> {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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<INodePropertyOptions[]> {
|
||||
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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']);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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<T>(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) =>
|
||||
<T, U>(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) =>
|
||||
<T, U>(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<T>(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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<INodeExecutionData> {
|
||||
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<IBinaryData[]> => {
|
||||
|
@ -599,39 +634,3 @@ export class EmailReadImapV1 implements INodeType {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function parseRawEmail(
|
||||
this: ITriggerFunctions,
|
||||
messageEncoded: ParserSource,
|
||||
dataPropertyNameDownload: string,
|
||||
): Promise<INodeExecutionData> {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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<INodeExecutionData> {
|
||||
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<IBinaryData[]> => {
|
||||
|
@ -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<INodeExecutionData> {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -38,7 +38,7 @@ async function execPromise(command: string): Promise<IExecReturnData> {
|
|||
|
||||
resolve(returnData);
|
||||
}).on('exit', (code) => {
|
||||
returnData.exitCode = code || 0;
|
||||
returnData.exitCode = code ?? 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<any> {
|
||||
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<any> {
|
|||
}
|
||||
}
|
||||
|
||||
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<any> {
|
|||
}
|
||||
}
|
||||
|
||||
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<any> {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<string | ftpClient.ListingElement> =
|
||||
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<string | ftpClient.ListingElement> =
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,26 @@ import {
|
|||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
async function processCampaignSearchResponse(
|
||||
this: IExecuteSingleFunctions,
|
||||
_inputData: INodeExecutionData[],
|
||||
responseData: IN8nHttpFullResponse,
|
||||
): Promise<INodeExecutionData[]> {
|
||||
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<INodeExecutionData[]> {
|
||||
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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -8,6 +8,54 @@ import moment from 'moment-timezone';
|
|||
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
|
||||
async function getAccessToken(
|
||||
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
credentials: IDataObject,
|
||||
): Promise<IDataObject> {
|
||||
//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<IDataObject> {
|
||||
//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) {
|
||||
|
|
|
@ -15,6 +15,55 @@ interface IGoogleAuthCredentials {
|
|||
privateKey: string;
|
||||
}
|
||||
|
||||
async function getAccessToken(
|
||||
this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions,
|
||||
credentials: IGoogleAuthCredentials,
|
||||
): Promise<IDataObject> {
|
||||
//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<IDataObject> {
|
||||
//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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -21,6 +21,59 @@ interface IGoogleAuthCredentials {
|
|||
privateKey: string;
|
||||
}
|
||||
|
||||
export async function getAccessToken(
|
||||
this:
|
||||
| IExecuteFunctions
|
||||
| IExecuteSingleFunctions
|
||||
| ILoadOptionsFunctions
|
||||
| ICredentialTestFunctions,
|
||||
credentials: IGoogleAuthCredentials,
|
||||
): Promise<IDataObject> {
|
||||
//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<IDataObject> {
|
||||
//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 {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<any> {
|
||||
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<any> {
|
||||
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-_]+)/');
|
||||
|
|
|
@ -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<any> {
|
||||
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<any> {
|
||||
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<any> {
|
||||
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<any> {
|
||||
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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<IDataObject> {
|
||||
//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<IDataObject> {
|
||||
//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,
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -15,6 +15,63 @@ export interface IGoogleAuthCredentials {
|
|||
privateKey: string;
|
||||
}
|
||||
|
||||
export async function getAccessToken(
|
||||
this:
|
||||
| IExecuteFunctions
|
||||
| IExecuteSingleFunctions
|
||||
| ILoadOptionsFunctions
|
||||
| ICredentialTestFunctions,
|
||||
credentials: IGoogleAuthCredentials,
|
||||
): Promise<IDataObject> {
|
||||
//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<IDataObject> {
|
||||
//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;
|
||||
});
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue