mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-13 16:14:07 -08:00
🔀 Merge master
This commit is contained in:
commit
3f03b59f0a
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -1,3 +1,23 @@
|
|||
# [0.212.0](https://github.com/n8n-io/n8n/compare/n8n@0.211.2...n8n@0.212.0) (2023-01-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Revert rule @typescript-eslint/prefer-nullish-coalescing ([e667df7](https://github.com/n8n-io/n8n/commit/e667df783c8c396fc40ff14de704b1e0def4a699))
|
||||
* **editor:** Allow special chars in node selector completion ([#5196](https://github.com/n8n-io/n8n/issues/5196)) ([b718464](https://github.com/n8n-io/n8n/commit/b718464b1f28e52ffb0b12e4b927d8fe3678d02a))
|
||||
* **GitLab Node:** Update credential test endpoint ([#5166](https://github.com/n8n-io/n8n/issues/5166)) ([e275306](https://github.com/n8n-io/n8n/commit/e275306c64a410c154e586532e35d25a583f75b4))
|
||||
* **Gmail Trigger Node:** Filter by labels not working ([#5173](https://github.com/n8n-io/n8n/issues/5173)) ([026f3a5](https://github.com/n8n-io/n8n/commit/026f3a532d30dcf79b76c9f9cff709e6af0eb9ee))
|
||||
* **HTTP Request Node:** Bug - node requires string instead of json ([8f49f49](https://github.com/n8n-io/n8n/commit/8f49f494ae66ce933f0fce3c3b43ce99baa1b728))
|
||||
* **HTTP Request Node:** Response format to text is ignored for JSON responses ([8dbe615](https://github.com/n8n-io/n8n/commit/8dbe6159d04c963e7858d31d64721ddc0911ea36))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Add Prometheus metrics for n8n events and api invocations (experimental) ([#5177](https://github.com/n8n-io/n8n/issues/5177)) ([9b032d6](https://github.com/n8n-io/n8n/commit/9b032d68bc8a7a45aae73e9442315e872902d50a)), closes [#5187](https://github.com/n8n-io/n8n/issues/5187)
|
||||
* **Item Lists Node:** Table tranformation ([5426690](https://github.com/n8n-io/n8n/commit/5426690791ead70085681ef31f229fbe15c7d656))
|
||||
|
||||
|
||||
|
||||
## [0.211.2](https://github.com/n8n-io/n8n/compare/n8n@0.211.1...n8n@0.211.2) (2023-01-17)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n",
|
||||
"version": "0.211.2",
|
||||
"version": "0.212.0",
|
||||
"private": true,
|
||||
"homepage": "https://n8n.io",
|
||||
"engines": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n",
|
||||
"version": "0.211.2",
|
||||
"version": "0.212.0",
|
||||
"description": "n8n Workflow Automation Tool",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -136,6 +136,7 @@
|
|||
"dotenv": "^8.0.0",
|
||||
"express": "^4.16.4",
|
||||
"express-openapi-validator": "^4.13.6",
|
||||
"express-prom-bundle": "^6.6.0",
|
||||
"fast-glob": "^3.2.5",
|
||||
"flatted": "^3.2.4",
|
||||
"google-timezones-json": "^1.0.2",
|
||||
|
@ -163,8 +164,8 @@
|
|||
"luxon": "^3.1.0",
|
||||
"mysql2": "~2.3.3",
|
||||
"n8n-core": "~0.151.1",
|
||||
"n8n-editor-ui": "~0.177.1",
|
||||
"n8n-nodes-base": "~0.209.2",
|
||||
"n8n-editor-ui": "~0.178.0",
|
||||
"n8n-nodes-base": "~0.210.0",
|
||||
"n8n-workflow": "~0.133.1",
|
||||
"nodemailer": "^6.7.1",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
|
|
|
@ -182,6 +182,7 @@ export class InternalHooksClass implements IInternalHooksClass {
|
|||
workflow: IWorkflowBase,
|
||||
nodeName: string,
|
||||
): Promise<void> {
|
||||
const nodeInWorkflow = workflow.nodes.find((node) => node.name === nodeName);
|
||||
void eventBus.sendNodeEvent({
|
||||
eventName: 'n8n.node.started',
|
||||
payload: {
|
||||
|
@ -189,6 +190,7 @@ export class InternalHooksClass implements IInternalHooksClass {
|
|||
nodeName,
|
||||
workflowId: workflow.id?.toString(),
|
||||
workflowName: workflow.name,
|
||||
nodeType: nodeInWorkflow?.type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -198,6 +200,7 @@ export class InternalHooksClass implements IInternalHooksClass {
|
|||
workflow: IWorkflowBase,
|
||||
nodeName: string,
|
||||
): Promise<void> {
|
||||
const nodeInWorkflow = workflow.nodes.find((node) => node.name === nodeName);
|
||||
void eventBus.sendNodeEvent({
|
||||
eventName: 'n8n.node.finished',
|
||||
payload: {
|
||||
|
@ -205,6 +208,7 @@ export class InternalHooksClass implements IInternalHooksClass {
|
|||
nodeName,
|
||||
workflowId: workflow.id?.toString(),
|
||||
workflowName: workflow.name,
|
||||
nodeType: nodeInWorkflow?.type,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -73,7 +73,6 @@ import jwt from 'jsonwebtoken';
|
|||
import jwks from 'jwks-rsa';
|
||||
// @ts-ignore
|
||||
import timezones from 'google-timezones-json';
|
||||
import promClient, { Registry } from 'prom-client';
|
||||
import history from 'connect-history-api-fallback';
|
||||
|
||||
import config from '@/config';
|
||||
|
@ -154,6 +153,7 @@ import { licenseController } from './license/license.controller';
|
|||
import { corsMiddleware } from './middlewares/cors';
|
||||
import { initEvents } from './events';
|
||||
import { AbstractServer } from './AbstractServer';
|
||||
import { configureMetrics } from './metrics';
|
||||
|
||||
const exec = promisify(callbackExec);
|
||||
|
||||
|
@ -321,15 +321,7 @@ class Server extends AbstractServer {
|
|||
}
|
||||
|
||||
async configure(): Promise<void> {
|
||||
const enableMetrics = config.getEnv('endpoints.metrics.enable');
|
||||
let register: Registry;
|
||||
|
||||
if (enableMetrics) {
|
||||
const prefix = config.getEnv('endpoints.metrics.prefix');
|
||||
register = new promClient.Registry();
|
||||
register.setDefaultLabels({ prefix });
|
||||
promClient.collectDefaultMetrics({ register });
|
||||
}
|
||||
configureMetrics(this.app);
|
||||
|
||||
this.frontendSettings.isNpmAvailable = await exec('npm --version')
|
||||
.then(() => true)
|
||||
|
@ -590,17 +582,6 @@ class Server extends AbstractServer {
|
|||
this.app.use(`/${this.restEndpoint}/nodes`, nodesController);
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// Metrics
|
||||
// ----------------------------------------
|
||||
if (enableMetrics) {
|
||||
this.app.get('/metrics', async (req: express.Request, res: express.Response) => {
|
||||
const response = await register.metrics();
|
||||
res.setHeader('Content-Type', register.contentType);
|
||||
ResponseHelper.sendSuccessResponse(res, response, true, 200);
|
||||
});
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
// Workflow
|
||||
// ----------------------------------------
|
||||
|
|
|
@ -88,7 +88,9 @@ export function executeErrorWorkflow(
|
|||
|
||||
let pastExecutionUrl: string | undefined;
|
||||
if (executionId !== undefined) {
|
||||
pastExecutionUrl = `${WebhookHelpers.getWebhookBaseUrl()}execution/${executionId}`;
|
||||
pastExecutionUrl = `${WebhookHelpers.getWebhookBaseUrl()}workflow/${
|
||||
workflowData.id
|
||||
}/executions/${executionId}`;
|
||||
}
|
||||
|
||||
if (fullRunData.data.resultData.error !== undefined) {
|
||||
|
|
|
@ -573,7 +573,7 @@ export const schema = {
|
|||
format: 'Boolean',
|
||||
default: false,
|
||||
env: 'N8N_METRICS',
|
||||
doc: 'Enable metrics endpoint',
|
||||
doc: 'Enable /metrics endpoint. Default: false',
|
||||
},
|
||||
prefix: {
|
||||
format: String,
|
||||
|
@ -581,6 +581,54 @@ export const schema = {
|
|||
env: 'N8N_METRICS_PREFIX',
|
||||
doc: 'An optional prefix for metric names. Default: n8n_',
|
||||
},
|
||||
includeDefaultMetrics: {
|
||||
format: Boolean,
|
||||
default: true,
|
||||
env: 'N8N_METRICS_INCLUDE_DEFAULT_METRICS',
|
||||
doc: 'Whether to expose default system and node.js metrics. Default: true',
|
||||
},
|
||||
includeWorkflowIdLabel: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
env: 'N8N_METRICS_INCLUDE_WORKFLOW_ID_LABEL',
|
||||
doc: 'Whether to include a label for the workflow ID on workflow metrics. Default: false',
|
||||
},
|
||||
includeNodeTypeLabel: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
env: 'N8N_METRICS_INCLUDE_NODE_TYPE_LABEL',
|
||||
doc: 'Whether to include a label for the node type on node metrics. Default: false',
|
||||
},
|
||||
includeCredentialTypeLabel: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
env: 'N8N_METRICS_INCLUDE_CREDENTIAL_TYPE_LABEL',
|
||||
doc: 'Whether to include a label for the credential type on credential metrics. Default: false',
|
||||
},
|
||||
includeApiEndpoints: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
env: 'N8N_METRICS_INCLUDE_API_ENDPOINTS',
|
||||
doc: 'Whether to expose metrics for API endpoints. Default: false',
|
||||
},
|
||||
includeApiPathLabel: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
env: 'N8N_METRICS_INCLUDE_API_PATH_LABEL',
|
||||
doc: 'Whether to include a label for the path of API invocations. Default: false',
|
||||
},
|
||||
includeApiMethodLabel: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
env: 'N8N_METRICS_INCLUDE_API_METHOD_LABEL',
|
||||
doc: 'Whether to include a label for the HTTP method (GET, POST, ...) of API invocations. Default: false',
|
||||
},
|
||||
includeApiStatusCodeLabel: {
|
||||
format: Boolean,
|
||||
default: false,
|
||||
env: 'N8N_METRICS_INCLUDE_API_STATUS_CODE_LABEL',
|
||||
doc: 'Whether to include a label for the HTTP status code (200, 404, ...) of API invocations. Default: false',
|
||||
},
|
||||
},
|
||||
rest: {
|
||||
format: String,
|
||||
|
|
|
@ -35,6 +35,11 @@ export interface EventPayloadAudit extends AbstractEventPayload {
|
|||
userEmail?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
credentialName?: string;
|
||||
credentialType?: string;
|
||||
credentialId?: string;
|
||||
workflowId?: string;
|
||||
workflowName?: string;
|
||||
}
|
||||
|
||||
export interface EventMessageAuditOptions extends AbstractEventMessageOptions {
|
||||
|
|
|
@ -11,6 +11,11 @@ export type EventNamesNodeType = typeof eventNamesNode[number];
|
|||
// --------------------------------------
|
||||
export interface EventPayloadNode extends AbstractEventPayload {
|
||||
msg?: string;
|
||||
executionId: string;
|
||||
nodeName: string;
|
||||
workflowId?: string;
|
||||
workflowName: string;
|
||||
nodeType?: string;
|
||||
}
|
||||
|
||||
export interface EventMessageNodeOptions extends AbstractEventMessageOptions {
|
||||
|
|
|
@ -6,7 +6,10 @@ import { MessageEventBusLogWriter } from '../MessageEventBusWriter/MessageEventB
|
|||
import EventEmitter from 'events';
|
||||
import config from '@/config';
|
||||
import * as Db from '@/Db';
|
||||
import { messageEventBusDestinationFromDb } from '../MessageEventBusDestination/Helpers.ee';
|
||||
import {
|
||||
messageEventBusDestinationFromDb,
|
||||
incrementPrometheusMetric,
|
||||
} from '../MessageEventBusDestination/Helpers.ee';
|
||||
import uniqby from 'lodash.uniqby';
|
||||
import { EventMessageConfirmSource } from '../EventMessageClasses/EventMessageConfirm';
|
||||
import {
|
||||
|
@ -205,6 +208,10 @@ class MessageEventBus extends EventEmitter {
|
|||
}
|
||||
|
||||
private async emitMessage(msg: EventMessageTypes) {
|
||||
if (config.getEnv('endpoints.metrics.enable')) {
|
||||
await incrementPrometheusMetric(msg);
|
||||
}
|
||||
|
||||
// generic emit for external modules to capture events
|
||||
// this is for internal use ONLY and not for use with custom destinations!
|
||||
this.emit('message', msg);
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
/* eslint-disable import/no-cycle */
|
||||
import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
|
||||
import type { EventDestinations } from '@/databases/entities/MessageEventBusDestinationEntity';
|
||||
import { promClient } from '@/metrics';
|
||||
import {
|
||||
EventMessageTypeNames,
|
||||
LoggerProxy,
|
||||
MessageEventBusDestinationTypeNames,
|
||||
} from 'n8n-workflow';
|
||||
import config from '../../config';
|
||||
import type { EventMessageTypes } from '../EventMessageClasses';
|
||||
import type { MessageEventBusDestination } from './MessageEventBusDestination.ee';
|
||||
import { MessageEventBusDestinationSentry } from './MessageEventBusDestinationSentry.ee';
|
||||
import { MessageEventBusDestinationSyslog } from './MessageEventBusDestinationSyslog.ee';
|
||||
|
@ -24,3 +31,85 @@ export function messageEventBusDestinationFromDb(
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const prometheusCounters: Record<string, promClient.Counter<string> | null> = {};
|
||||
|
||||
function getMetricNameForEvent(event: EventMessageTypes): string {
|
||||
const prefix = config.getEnv('endpoints.metrics.prefix');
|
||||
return prefix + event.eventName.replace('n8n.', '').replace(/\./g, '_') + '_total';
|
||||
}
|
||||
|
||||
function getLabelValueForNode(nodeType: string): string {
|
||||
return nodeType.replace('n8n-nodes-', '').replace(/\./g, '_');
|
||||
}
|
||||
|
||||
function getLabelValueForCredential(credentialType: string): string {
|
||||
return credentialType.replace(/\./g, '_');
|
||||
}
|
||||
|
||||
function getLabelsForEvent(event: EventMessageTypes): Record<string, string> {
|
||||
switch (event.__type) {
|
||||
case EventMessageTypeNames.audit:
|
||||
if (event.eventName.startsWith('n8n.audit.user.credentials')) {
|
||||
return config.getEnv('endpoints.metrics.includeCredentialTypeLabel')
|
||||
? {
|
||||
credential_type: getLabelValueForCredential(
|
||||
event.payload.credentialType ?? 'unknown',
|
||||
),
|
||||
}
|
||||
: {};
|
||||
}
|
||||
|
||||
if (event.eventName.startsWith('n8n.audit.workflow')) {
|
||||
return config.getEnv('endpoints.metrics.includeWorkflowIdLabel')
|
||||
? { workflow_id: event.payload.workflowId?.toString() ?? 'unknown' }
|
||||
: {};
|
||||
}
|
||||
break;
|
||||
|
||||
case EventMessageTypeNames.node:
|
||||
return config.getEnv('endpoints.metrics.includeNodeTypeLabel')
|
||||
? { node_type: getLabelValueForNode(event.payload.nodeType ?? 'unknown') }
|
||||
: {};
|
||||
|
||||
case EventMessageTypeNames.workflow:
|
||||
return config.getEnv('endpoints.metrics.includeWorkflowIdLabel')
|
||||
? { workflow_id: event.payload.workflowId?.toString() ?? 'unknown' }
|
||||
: {};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function getCounterSingletonForEvent(event: EventMessageTypes) {
|
||||
if (!prometheusCounters[event.eventName]) {
|
||||
const metricName = getMetricNameForEvent(event);
|
||||
|
||||
if (!promClient.validateMetricName(metricName)) {
|
||||
LoggerProxy.debug(`Invalid metric name: ${metricName}. Ignoring it!`);
|
||||
prometheusCounters[event.eventName] = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
const counter = new promClient.Counter({
|
||||
name: metricName,
|
||||
help: `Total number of ${event.eventName} events.`,
|
||||
labelNames: Object.keys(getLabelsForEvent(event)),
|
||||
});
|
||||
|
||||
promClient.register.registerMetric(counter);
|
||||
prometheusCounters[event.eventName] = counter;
|
||||
}
|
||||
|
||||
return prometheusCounters[event.eventName];
|
||||
}
|
||||
|
||||
export async function incrementPrometheusMetric(event: EventMessageTypes): Promise<void> {
|
||||
const counter = getCounterSingletonForEvent(event);
|
||||
|
||||
if (!counter) {
|
||||
return;
|
||||
}
|
||||
|
||||
counter.inc(getLabelsForEvent(event));
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import {
|
|||
} from 'n8n-workflow';
|
||||
import { User } from '../databases/entities/User';
|
||||
import * as ResponseHelper from '@/ResponseHelper';
|
||||
import { EventMessageNode, EventMessageNodeOptions } from './EventMessageClasses/EventMessageNode';
|
||||
|
||||
export const eventBusRouter = express.Router();
|
||||
|
||||
|
@ -116,6 +117,9 @@ eventBusRouter.post(
|
|||
case EventMessageTypeNames.audit:
|
||||
msg = new EventMessageAudit(req.body as EventMessageAuditOptions);
|
||||
break;
|
||||
case EventMessageTypeNames.node:
|
||||
msg = new EventMessageNode(req.body as EventMessageNodeOptions);
|
||||
break;
|
||||
case EventMessageTypeNames.generic:
|
||||
default:
|
||||
msg = new EventMessageGeneric(req.body);
|
||||
|
|
71
packages/cli/src/metrics/index.ts
Normal file
71
packages/cli/src/metrics/index.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* eslint-disable @typescript-eslint/no-use-before-define */
|
||||
import config from '@/config';
|
||||
import { N8N_VERSION } from '@/constants';
|
||||
import * as ResponseHelper from '@/ResponseHelper';
|
||||
import express from 'express';
|
||||
import promBundle from 'express-prom-bundle';
|
||||
import promClient from 'prom-client';
|
||||
import semverParse from 'semver/functions/parse';
|
||||
|
||||
export { promClient };
|
||||
|
||||
export function configureMetrics(app: express.Application) {
|
||||
if (!config.getEnv('endpoints.metrics.enable')) {
|
||||
return;
|
||||
}
|
||||
|
||||
setupDefaultMetrics();
|
||||
setupN8nVersionMetric();
|
||||
setupApiMetrics(app);
|
||||
mountMetricsEndpoint(app);
|
||||
}
|
||||
|
||||
function setupN8nVersionMetric() {
|
||||
const n8nVersion = semverParse(N8N_VERSION || '0.0.0');
|
||||
|
||||
if (n8nVersion) {
|
||||
const versionGauge = new promClient.Gauge({
|
||||
name: config.getEnv('endpoints.metrics.prefix') + 'version_info',
|
||||
help: 'n8n version info.',
|
||||
labelNames: ['version', 'major', 'minor', 'patch'],
|
||||
});
|
||||
|
||||
versionGauge.set(
|
||||
{
|
||||
version: 'v' + n8nVersion.version,
|
||||
major: n8nVersion.major,
|
||||
minor: n8nVersion.minor,
|
||||
patch: n8nVersion.patch,
|
||||
},
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setupDefaultMetrics() {
|
||||
if (config.getEnv('endpoints.metrics.includeDefaultMetrics')) {
|
||||
promClient.collectDefaultMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
function setupApiMetrics(app: express.Application) {
|
||||
if (config.getEnv('endpoints.metrics.includeApiEndpoints')) {
|
||||
const metricsMiddleware = promBundle({
|
||||
autoregister: false,
|
||||
includeUp: false,
|
||||
includePath: config.getEnv('endpoints.metrics.includeApiPathLabel'),
|
||||
includeMethod: config.getEnv('endpoints.metrics.includeApiMethodLabel'),
|
||||
includeStatusCode: config.getEnv('endpoints.metrics.includeApiStatusCodeLabel'),
|
||||
});
|
||||
|
||||
app.use(['/rest/', '/webhook/', 'webhook-test/', '/api/'], metricsMiddleware);
|
||||
}
|
||||
}
|
||||
|
||||
function mountMetricsEndpoint(app: express.Application) {
|
||||
app.get('/metrics', async (req: express.Request, res: express.Response) => {
|
||||
const response = await promClient.register.metrics();
|
||||
res.setHeader('Content-Type', promClient.register.contentType);
|
||||
ResponseHelper.sendSuccessResponse(res, response, true, 200);
|
||||
});
|
||||
}
|
|
@ -178,7 +178,7 @@ test('GET /eventbus/destination all returned destinations should exist in eventb
|
|||
}
|
||||
});
|
||||
|
||||
test('should send message to syslog ', async () => {
|
||||
test.skip('should send message to syslog', async () => {
|
||||
const testMessage = new EventMessageGeneric({ eventName: 'n8n.test.message', id: uuid() });
|
||||
config.set('enterprise.features.logStreaming', true);
|
||||
// await cleanLogs();
|
||||
|
@ -217,7 +217,7 @@ test('should send message to syslog ', async () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('should confirm send message if there are no subscribers', async () => {
|
||||
test.skip('should confirm send message if there are no subscribers', async () => {
|
||||
const testMessageUnsubscribed = new EventMessageGeneric({
|
||||
eventName: 'n8n.test.unsub',
|
||||
id: uuid(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-editor-ui",
|
||||
"version": "0.177.1",
|
||||
"version": "0.178.0",
|
||||
"description": "Workflow Editor UI for n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
|
54
packages/editor-ui/src/api/eventbus.ee.ts
Normal file
54
packages/editor-ui/src/api/eventbus.ee.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { IRestApiContext } from '@/Interface';
|
||||
import { makeRestApiRequest } from '@/utils';
|
||||
import { IDataObject, MessageEventBusDestinationOptions } from 'n8n-workflow';
|
||||
|
||||
export async function saveDestinationToDb(
|
||||
context: IRestApiContext,
|
||||
destination: MessageEventBusDestinationOptions,
|
||||
subscribedEvents: string[] = [],
|
||||
) {
|
||||
if (destination.id) {
|
||||
const data: IDataObject = {
|
||||
...destination,
|
||||
subscribedEvents,
|
||||
};
|
||||
return makeRestApiRequest(context, 'POST', '/eventbus/destination', data);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteDestinationFromDb(context: IRestApiContext, destinationId: string) {
|
||||
return makeRestApiRequest(context, 'DELETE', `/eventbus/destination?id=${destinationId}`);
|
||||
}
|
||||
|
||||
export async function sendTestMessageToDestination(
|
||||
context: IRestApiContext,
|
||||
destination: MessageEventBusDestinationOptions,
|
||||
) {
|
||||
if (destination.id) {
|
||||
const data: IDataObject = {
|
||||
...destination,
|
||||
};
|
||||
return makeRestApiRequest(context, 'GET', '/eventbus/testmessage', data);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getEventNamesFromBackend(context: IRestApiContext): Promise<string[]> {
|
||||
return makeRestApiRequest(context, 'GET', '/eventbus/eventnames');
|
||||
}
|
||||
|
||||
export async function getDestinationsFromBackend(
|
||||
context: IRestApiContext,
|
||||
): Promise<MessageEventBusDestinationOptions[]> {
|
||||
return makeRestApiRequest(context, 'GET', '/eventbus/destination');
|
||||
}
|
||||
|
||||
export async function getExecutionEvents(context: IRestApiContext, executionId: string) {
|
||||
return makeRestApiRequest(context, 'GET', `/eventbus/execution/${executionId}`);
|
||||
}
|
||||
|
||||
export async function recoverExecutionDataFromEvents(
|
||||
context: IRestApiContext,
|
||||
executionId: string,
|
||||
) {
|
||||
return makeRestApiRequest(context, 'GET', `/eventbus/execution-recover/${executionId}`);
|
||||
}
|
|
@ -51,7 +51,6 @@ import mixins from 'vue-typed-mixins';
|
|||
import { EnterpriseEditionFeature } from '@/constants';
|
||||
import { showMessage } from '@/mixins/showMessage';
|
||||
import { useLogStreamingStore } from '../../stores/logStreamingStore';
|
||||
import { restApi } from '@/mixins/restApi';
|
||||
import Vue from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import {
|
||||
|
@ -59,7 +58,6 @@ import {
|
|||
defaultMessageEventBusDestinationOptions,
|
||||
MessageEventBusDestinationOptions,
|
||||
} from 'n8n-workflow';
|
||||
import { saveDestinationToDb } from './Helpers.ee';
|
||||
import { BaseTextKey } from '../../plugins/i18n';
|
||||
|
||||
export const DESTINATION_LIST_ITEM_ACTIONS = {
|
||||
|
@ -67,7 +65,7 @@ export const DESTINATION_LIST_ITEM_ACTIONS = {
|
|||
DELETE: 'delete',
|
||||
};
|
||||
|
||||
export default mixins(showMessage, restApi).extend({
|
||||
export default mixins(showMessage).extend({
|
||||
data() {
|
||||
return {
|
||||
EnterpriseEditionFeature,
|
||||
|
@ -142,7 +140,7 @@ export default mixins(showMessage, restApi).extend({
|
|||
this.saveDestination();
|
||||
},
|
||||
async saveDestination() {
|
||||
await saveDestinationToDb(this.restApi(), this.nodeParameters);
|
||||
await this.logStreamingStore.saveDestination(this.nodeParameters);
|
||||
},
|
||||
async onAction(action: string) {
|
||||
if (action === DESTINATION_LIST_ITEM_ACTIONS.OPEN) {
|
||||
|
|
|
@ -178,7 +178,6 @@ import mixins from 'vue-typed-mixins';
|
|||
import { useLogStreamingStore } from '../../stores/logStreamingStore';
|
||||
import { useNDVStore } from '../../stores/ndv';
|
||||
import { useWorkflowsStore } from '../../stores/workflows';
|
||||
import { restApi } from '../../mixins/restApi';
|
||||
import ParameterInputList from '@/components/ParameterInputList.vue';
|
||||
import NodeCredentials from '@/components/NodeCredentials.vue';
|
||||
import { IMenuItem, INodeUi, ITab, IUpdateInformation } from '../../Interface';
|
||||
|
@ -200,7 +199,7 @@ import Modal from '@/components/Modal.vue';
|
|||
import { showMessage } from '@/mixins/showMessage';
|
||||
import { useUIStore } from '../../stores/ui';
|
||||
import { useUsersStore } from '../../stores/users';
|
||||
import { destinationToFakeINodeUi, saveDestinationToDb, sendTestMessage } from './Helpers.ee';
|
||||
import { destinationToFakeINodeUi } from './Helpers.ee';
|
||||
import {
|
||||
webhookModalDescription,
|
||||
sentryModalDescription,
|
||||
|
@ -212,7 +211,7 @@ import SaveButton from '../SaveButton.vue';
|
|||
import EventSelection from '@/components/SettingsLogStreaming/EventSelection.ee.vue';
|
||||
import { Checkbox } from 'element-ui';
|
||||
|
||||
export default mixins(showMessage, restApi).extend({
|
||||
export default mixins(showMessage).extend({
|
||||
name: 'event-destination-settings-modal',
|
||||
props: {
|
||||
modalName: String,
|
||||
|
@ -427,12 +426,14 @@ export default mixins(showMessage, restApi).extend({
|
|||
this.nodeParameters = deepCopy(nodeParameters);
|
||||
this.workflowsStore.updateNodeProperties({
|
||||
name: this.node.name,
|
||||
properties: { parameters: this.nodeParameters as unknown as IDataObject },
|
||||
properties: { parameters: this.nodeParameters as unknown as IDataObject, position: [0, 0] },
|
||||
});
|
||||
this.logStreamingStore.updateDestination(this.nodeParameters);
|
||||
if (this.hasOnceBeenSaved) {
|
||||
this.logStreamingStore.updateDestination(this.nodeParameters);
|
||||
}
|
||||
},
|
||||
async sendTestEvent() {
|
||||
this.testMessageResult = await sendTestMessage(this.restApi(), this.nodeParameters);
|
||||
this.testMessageResult = await this.logStreamingStore.sendTestMessage(this.nodeParameters);
|
||||
this.testMessageSent = true;
|
||||
},
|
||||
async removeThis() {
|
||||
|
@ -467,12 +468,14 @@ export default mixins(showMessage, restApi).extend({
|
|||
if (this.unchanged || !this.destination.id) {
|
||||
return;
|
||||
}
|
||||
await saveDestinationToDb(this.restApi(), this.nodeParameters);
|
||||
this.hasOnceBeenSaved = true;
|
||||
this.testMessageSent = false;
|
||||
this.unchanged = true;
|
||||
this.$props.eventBus.$emit('destinationWasSaved', this.destination.id);
|
||||
this.uiStore.stateIsDirty = false;
|
||||
const saveResult = await this.logStreamingStore.saveDestination(this.nodeParameters);
|
||||
if (saveResult === true) {
|
||||
this.hasOnceBeenSaved = true;
|
||||
this.testMessageSent = false;
|
||||
this.unchanged = true;
|
||||
this.$props.eventBus.$emit('destinationWasSaved', this.destination.id);
|
||||
this.uiStore.stateIsDirty = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { INodeCredentials, INodeParameters, MessageEventBusDestinationOptions } from 'n8n-workflow';
|
||||
import { INodeUi, IRestApi } from '../../Interface';
|
||||
import { useLogStreamingStore } from '../../stores/logStreamingStore';
|
||||
import { INodeUi } from '../../Interface';
|
||||
|
||||
export function destinationToFakeINodeUi(
|
||||
destination: MessageEventBusDestinationOptions,
|
||||
|
@ -20,39 +19,3 @@ export function destinationToFakeINodeUi(
|
|||
},
|
||||
} as INodeUi;
|
||||
}
|
||||
|
||||
export async function saveDestinationToDb(
|
||||
restApi: IRestApi,
|
||||
destination: MessageEventBusDestinationOptions,
|
||||
) {
|
||||
const logStreamingStore = useLogStreamingStore();
|
||||
if (destination.id) {
|
||||
const data: MessageEventBusDestinationOptions = {
|
||||
...destination,
|
||||
subscribedEvents: logStreamingStore.getSelectedEvents(destination.id),
|
||||
};
|
||||
try {
|
||||
await restApi.makeRestApiRequest('POST', '/eventbus/destination', data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
logStreamingStore.updateDestination(destination);
|
||||
}
|
||||
}
|
||||
|
||||
export async function sendTestMessage(
|
||||
restApi: IRestApi,
|
||||
destination: MessageEventBusDestinationOptions,
|
||||
) {
|
||||
if (destination.id) {
|
||||
try {
|
||||
const sendResult = await restApi.makeRestApiRequest('GET', '/eventbus/testmessage', {
|
||||
id: destination.id,
|
||||
});
|
||||
return sendResult;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
import { deepCopy, MessageEventBusDestinationOptions } from 'n8n-workflow';
|
||||
import { defineStore } from 'pinia';
|
||||
import {
|
||||
deleteDestinationFromDb,
|
||||
getDestinationsFromBackend,
|
||||
getEventNamesFromBackend,
|
||||
saveDestinationToDb,
|
||||
sendTestMessageToDestination,
|
||||
} from '../api/eventbus.ee';
|
||||
import { useRootStore } from './n8nRootStore';
|
||||
|
||||
export interface EventSelectionItem {
|
||||
selected: boolean;
|
||||
|
@ -8,18 +16,19 @@ export interface EventSelectionItem {
|
|||
label: string;
|
||||
}
|
||||
|
||||
export interface EventSelectionGroup extends EventSelectionItem {
|
||||
interface EventSelectionGroup extends EventSelectionItem {
|
||||
children: EventSelectionItem[];
|
||||
}
|
||||
|
||||
export interface TreeAndSelectionStoreItem {
|
||||
interface DestinationStoreItem {
|
||||
destination: MessageEventBusDestinationOptions;
|
||||
selectedEvents: Set<string>;
|
||||
eventGroups: EventSelectionGroup[];
|
||||
isNew: boolean;
|
||||
}
|
||||
|
||||
export interface DestinationSettingsStore {
|
||||
[key: string]: TreeAndSelectionStoreItem;
|
||||
[key: string]: DestinationStoreItem;
|
||||
}
|
||||
|
||||
export const useLogStreamingStore = defineStore('logStreaming', {
|
||||
|
@ -51,13 +60,15 @@ export const useLogStreamingStore = defineStore('logStreaming', {
|
|||
return destinations;
|
||||
},
|
||||
updateDestination(destination: MessageEventBusDestinationOptions) {
|
||||
this.$patch((state) => {
|
||||
if (destination.id && destination.id in this.items) {
|
||||
state.items[destination.id].destination = destination;
|
||||
}
|
||||
// to trigger refresh
|
||||
state.items = deepCopy(state.items);
|
||||
});
|
||||
if (destination.id && destination.id in this.items) {
|
||||
this.$patch((state) => {
|
||||
if (destination.id && destination.id in this.items) {
|
||||
state.items[destination.id].destination = destination;
|
||||
}
|
||||
// to trigger refresh
|
||||
state.items = deepCopy(state.items);
|
||||
});
|
||||
}
|
||||
},
|
||||
removeDestination(destinationId: string) {
|
||||
if (!destinationId) return;
|
||||
|
@ -159,7 +170,8 @@ export const useLogStreamingStore = defineStore('logStreaming', {
|
|||
destination,
|
||||
selectedEvents: new Set<string>(),
|
||||
eventGroups: [],
|
||||
} as TreeAndSelectionStoreItem;
|
||||
isNew: false,
|
||||
} as DestinationStoreItem;
|
||||
}
|
||||
this.items[destination.id]?.selectedEvents?.clear();
|
||||
if (destination.subscribedEvents) {
|
||||
|
@ -173,6 +185,44 @@ export const useLogStreamingStore = defineStore('logStreaming', {
|
|||
);
|
||||
}
|
||||
},
|
||||
async saveDestination(destination: MessageEventBusDestinationOptions): Promise<boolean> {
|
||||
if (destination.id) {
|
||||
const rootStore = useRootStore();
|
||||
const selectedEvents = this.getSelectedEvents(destination.id);
|
||||
try {
|
||||
await saveDestinationToDb(rootStore.getRestApiContext, destination, selectedEvents);
|
||||
this.updateDestination(destination);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async sendTestMessage(destination: MessageEventBusDestinationOptions) {
|
||||
if (destination.id) {
|
||||
const rootStore = useRootStore();
|
||||
const testResult = await sendTestMessageToDestination(
|
||||
rootStore.getRestApiContext,
|
||||
destination,
|
||||
);
|
||||
return testResult;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async fetchEventNames(): Promise<string[]> {
|
||||
const rootStore = useRootStore();
|
||||
return getEventNamesFromBackend(rootStore.getRestApiContext);
|
||||
},
|
||||
async fetchDestinations(): Promise<MessageEventBusDestinationOptions[]> {
|
||||
const rootStore = useRootStore();
|
||||
return getDestinationsFromBackend(rootStore.getRestApiContext);
|
||||
},
|
||||
async deleteDestination(destinationId: string) {
|
||||
const rootStore = useRootStore();
|
||||
await deleteDestinationFromDb(rootStore.getRestApiContext, destinationId);
|
||||
this.removeDestination(destinationId);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -90,7 +90,6 @@ import { useSettingsStore } from '../stores/settings';
|
|||
import { useUIStore } from '../stores/ui';
|
||||
import { LOG_STREAM_MODAL_KEY, EnterpriseEditionFeature } from '../constants';
|
||||
import Vue from 'vue';
|
||||
import { restApi } from '../mixins/restApi';
|
||||
import {
|
||||
deepCopy,
|
||||
defaultMessageEventBusDestinationOptions,
|
||||
|
@ -99,7 +98,7 @@ import {
|
|||
import PageViewLayout from '@/components/layouts/PageViewLayout.vue';
|
||||
import EventDestinationCard from '@/components/SettingsLogStreaming/EventDestinationCard.ee.vue';
|
||||
|
||||
export default mixins(restApi).extend({
|
||||
export default mixins().extend({
|
||||
name: 'SettingsLogStreamingView',
|
||||
props: {},
|
||||
components: {
|
||||
|
@ -125,7 +124,7 @@ export default mixins(restApi).extend({
|
|||
this.uiStore.nodeViewInitialized = false;
|
||||
|
||||
// fetch Destination data from the backend
|
||||
await this.getDestinationDataFromREST();
|
||||
await this.getDestinationDataFromBackend();
|
||||
|
||||
// since we are not really integrated into the hooks, we listen to the store and refresh the destinations
|
||||
this.logStreamingStore.$onAction(({ name, after }) => {
|
||||
|
@ -174,18 +173,18 @@ export default mixins(restApi).extend({
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
async getDestinationDataFromREST(): Promise<any> {
|
||||
async getDestinationDataFromBackend(): Promise<void> {
|
||||
this.logStreamingStore.clearEventNames();
|
||||
this.logStreamingStore.clearDestinationItemTrees();
|
||||
this.allDestinations = [];
|
||||
const eventNamesData = await this.restApi().makeRestApiRequest('get', '/eventbus/eventnames');
|
||||
const eventNamesData = await this.logStreamingStore.fetchEventNames();
|
||||
if (eventNamesData) {
|
||||
for (const eventName of eventNamesData) {
|
||||
this.logStreamingStore.addEventName(eventName);
|
||||
}
|
||||
}
|
||||
const destinationData: MessageEventBusDestinationOptions[] =
|
||||
await this.restApi().makeRestApiRequest('get', '/eventbus/destination');
|
||||
await this.logStreamingStore.fetchDestinations();
|
||||
if (destinationData) {
|
||||
for (const destination of destinationData) {
|
||||
this.logStreamingStore.addDestination(destination);
|
||||
|
@ -218,11 +217,7 @@ export default mixins(restApi).extend({
|
|||
},
|
||||
async onRemove(destinationId?: string) {
|
||||
if (!destinationId) return;
|
||||
await this.restApi().makeRestApiRequest(
|
||||
'DELETE',
|
||||
`/eventbus/destination?id=${destinationId}`,
|
||||
);
|
||||
this.logStreamingStore.removeDestination(destinationId);
|
||||
await this.logStreamingStore.deleteDestination(destinationId);
|
||||
const foundNode = this.workflowsStore.getNodeByName(destinationId);
|
||||
if (foundNode) {
|
||||
this.workflowsStore.removeNode(foundNode);
|
||||
|
|
|
@ -29,6 +29,7 @@ module.exports = {
|
|||
'@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 }],
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
},
|
||||
|
||||
overrides: [
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ICredentialType, INodeProperties } from 'n8n-workflow';
|
||||
import { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export class LinearApi implements ICredentialType {
|
||||
name = 'linearApi';
|
||||
|
@ -16,4 +16,13 @@ export class LinearApi implements ICredentialType {
|
|||
default: '',
|
||||
},
|
||||
];
|
||||
|
||||
authenticate: IAuthenticateGeneric = {
|
||||
type: 'generic',
|
||||
properties: {
|
||||
headers: {
|
||||
Authorization: '={{$credentials.apiKey}}',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ export async function calendlyApiRequest(
|
|||
method,
|
||||
body,
|
||||
qs: query,
|
||||
uri: uri ?? `${endpoint}${resource}`,
|
||||
uri: uri || `${endpoint}${resource}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -430,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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ export async function erpNextApiRequest(
|
|||
method,
|
||||
body,
|
||||
qs: query,
|
||||
uri: uri ?? `${baseUrl}${resource}`,
|
||||
uri: uri || `${baseUrl}${resource}`,
|
||||
json: true,
|
||||
rejectUnauthorized: !credentials.allowUnauthorizedCerts,
|
||||
};
|
||||
|
|
|
@ -1239,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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ export class ErrorTrigger implements INodeType {
|
|||
items[0].json = {
|
||||
execution: {
|
||||
id,
|
||||
url: `${urlParts.join('/')}/${id}`,
|
||||
url: `${urlParts.join('/')}/workflow/1/${id}`,
|
||||
retryOf: '34',
|
||||
error: {
|
||||
message: 'Example Error Message',
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ async function getAccessToken(
|
|||
const signature = jwt.sign(
|
||||
{
|
||||
iss: credentials.email as string,
|
||||
sub: credentials.delegatedEmail ?? (credentials.email as string),
|
||||
sub: credentials.delegatedEmail || (credentials.email as string),
|
||||
scope: scopes.join(' '),
|
||||
aud: 'https://oauth2.googleapis.com/token',
|
||||
iat: now,
|
||||
|
@ -79,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 {
|
||||
|
|
|
@ -31,7 +31,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,
|
||||
|
@ -86,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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -41,7 +41,7 @@ export 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,
|
||||
|
@ -92,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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -35,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,
|
||||
|
@ -89,7 +89,7 @@ export async function googleApiRequest(
|
|||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: uri ?? `https://docs.googleapis.com/v1${endpoint}`,
|
||||
uri: uri || `https://docs.googleapis.com/v1${endpoint}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
|
|
|
@ -35,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,
|
||||
|
@ -91,7 +91,7 @@ export async function googleApiRequest(
|
|||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: uri ?? `https://www.googleapis.com${resource}`,
|
||||
uri: uri || `https://www.googleapis.com${resource}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
|
@ -136,8 +136,8 @@ export async function googleApiRequestAllItems(
|
|||
const returnData: IDataObject[] = [];
|
||||
|
||||
let responseData;
|
||||
query.maxResults = query.maxResults ?? 100;
|
||||
query.pageSize = query.pageSize ?? 100;
|
||||
query.maxResults = query.maxResults || 100;
|
||||
query.pageSize = query.pageSize || 100;
|
||||
|
||||
do {
|
||||
responseData = await googleApiRequest.call(this, method, endpoint, body, query);
|
||||
|
|
|
@ -2652,7 +2652,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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -116,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',
|
||||
},
|
||||
|
@ -528,7 +528,7 @@ export async function prepareEmailAttachments(
|
|||
}
|
||||
|
||||
attachmentsList.push({
|
||||
name: binaryData.fileName ?? 'unknown',
|
||||
name: binaryData.fileName || 'unknown',
|
||||
content: binaryDataBuffer,
|
||||
type: binaryData.mimeType,
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -490,8 +490,8 @@ export class GoogleSheetsTrigger implements INodeType {
|
|||
}
|
||||
|
||||
const [rangeFrom, rangeTo] = range.split(':');
|
||||
const cellDataFrom = rangeFrom.match(/([a-zA-Z]{1,10})([0-9]{0,10})/) ?? [];
|
||||
const cellDataTo = rangeTo.match(/([a-zA-Z]{1,10})([0-9]{0,10})/) ?? [];
|
||||
const cellDataFrom = rangeFrom.match(/([a-zA-Z]{1,10})([0-9]{0,10})/) || [];
|
||||
const cellDataTo = rangeTo.match(/([a-zA-Z]{1,10})([0-9]{0,10})/) || [];
|
||||
|
||||
if (rangeDefinition === 'specifyRangeA1' && cellDataFrom[2] !== undefined) {
|
||||
keyRange = `${cellDataFrom[1]}${+cellDataFrom[2]}:${cellDataTo[1]}${+cellDataFrom[2]}`;
|
||||
|
@ -544,7 +544,7 @@ export class GoogleSheetsTrigger implements INodeType {
|
|||
return null;
|
||||
}
|
||||
|
||||
const addedRows = sheetData?.slice(workflowStaticData.lastIndexChecked as number) ?? [];
|
||||
const addedRows = sheetData?.slice(workflowStaticData.lastIndexChecked as number) || [];
|
||||
const returnData = arrayOfArraysToJson(addedRows, columns);
|
||||
|
||||
workflowStaticData.lastIndexChecked = sheetData.length;
|
||||
|
|
|
@ -39,7 +39,7 @@ export 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,
|
||||
|
@ -94,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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -171,7 +171,7 @@ export async function execute(
|
|||
|
||||
const options = this.getNodeParameter('options', 0, {});
|
||||
|
||||
const valueRenderMode = (options.valueRenderMode ?? 'UNFORMATTED_VALUE') as ValueRenderOption;
|
||||
const valueRenderMode = (options.valueRenderMode || 'UNFORMATTED_VALUE') as ValueRenderOption;
|
||||
|
||||
const locationDefineOptions = (options.locationDefine as IDataObject)?.values as IDataObject;
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ export class GoogleSheet {
|
|||
// );
|
||||
|
||||
const lastRowWithData =
|
||||
lastRow ??
|
||||
lastRow ||
|
||||
(((await this.getData(range, 'UNFORMATTED_VALUE')) as string[][]) || []).length + 1;
|
||||
|
||||
const response = await this.updateRows(
|
||||
|
@ -403,10 +403,10 @@ export class GoogleSheet {
|
|||
columnValuesList = sheetData.slice(dataStartRowIndex - 1).map((row) => row[keyIndex]);
|
||||
} else {
|
||||
const decodedRange = this.getDecodedSheetRange(range);
|
||||
const startRowIndex = decodedRange.start?.row ?? dataStartRowIndex;
|
||||
const endRowIndex = decodedRange.end?.row ?? '';
|
||||
const startRowIndex = decodedRange.start?.row || dataStartRowIndex;
|
||||
const endRowIndex = decodedRange.end?.row || '';
|
||||
|
||||
const keyColumn = this.getColumnWithOffset(decodedRange.start?.column ?? 'A', keyIndex);
|
||||
const keyColumn = this.getColumnWithOffset(decodedRange.start?.column || 'A', keyIndex);
|
||||
const keyColumnRange = `${decodedRange.name}!${keyColumn}${startRowIndex}:${keyColumn}${endRowIndex}`;
|
||||
columnValuesList = await this.getData(keyColumnRange, valueRenderMode);
|
||||
}
|
||||
|
@ -446,9 +446,9 @@ export class GoogleSheet {
|
|||
) {
|
||||
const decodedRange = this.getDecodedSheetRange(range);
|
||||
// prettier-ignore
|
||||
const keyRowRange = `${decodedRange.name}!${decodedRange.start?.column ?? ''}${keyRowIndex + 1}:${decodedRange.end?.column ?? ''}${keyRowIndex + 1}`;
|
||||
const keyRowRange = `${decodedRange.name}!${decodedRange.start?.column || ''}${keyRowIndex + 1}:${decodedRange.end?.column || ''}${keyRowIndex + 1}`;
|
||||
|
||||
const sheetDatakeyRow = columnNamesList ?? (await this.getData(keyRowRange, valueRenderMode));
|
||||
const sheetDatakeyRow = columnNamesList || (await this.getData(keyRowRange, valueRenderMode));
|
||||
|
||||
if (sheetDatakeyRow === undefined) {
|
||||
throw new NodeOperationError(
|
||||
|
@ -469,7 +469,7 @@ export class GoogleSheet {
|
|||
}
|
||||
|
||||
const columnValues: Array<string | number> =
|
||||
columnValuesList ??
|
||||
columnValuesList ||
|
||||
(await this.getColumnValues(range, keyIndex, dataStartRowIndex, valueRenderMode));
|
||||
|
||||
const updateData: ISheetUpdateData[] = [];
|
||||
|
@ -527,7 +527,7 @@ export class GoogleSheet {
|
|||
// Property exists so add it to the data to update
|
||||
// Get the column name in which the property data can be found
|
||||
const columnToUpdate = this.getColumnWithOffset(
|
||||
decodedRange.start?.column ?? 'A',
|
||||
decodedRange.start?.column || 'A',
|
||||
columnNames.indexOf(name),
|
||||
);
|
||||
|
||||
|
@ -640,7 +640,7 @@ export class GoogleSheet {
|
|||
const decodedRange = this.getDecodedSheetRange(range);
|
||||
|
||||
const columnNamesRow =
|
||||
columnNamesList ??
|
||||
columnNamesList ||
|
||||
(await this.getData(
|
||||
`${decodedRange.name}!${keyRowIndex}:${keyRowIndex}`,
|
||||
'UNFORMATTED_VALUE',
|
||||
|
@ -704,7 +704,7 @@ export class GoogleSheet {
|
|||
}
|
||||
|
||||
private splitCellRange(cell: string, range: string): SheetCellDecoded {
|
||||
const cellData = cell.match(/([a-zA-Z]{1,10})([0-9]{0,10})/) ?? [];
|
||||
const cellData = cell.match(/([a-zA-Z]{1,10})([0-9]{0,10})/) || [];
|
||||
|
||||
if (cellData === null || cellData.length !== 3) {
|
||||
throw new NodeOperationError(
|
||||
|
|
|
@ -36,7 +36,7 @@ export 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,
|
||||
|
@ -91,7 +91,7 @@ export async function apiRequest(
|
|||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: uri ?? `https://sheets.googleapis.com${resource}`,
|
||||
uri: uri || `https://sheets.googleapis.com${resource}`,
|
||||
json: true,
|
||||
...option,
|
||||
};
|
||||
|
|
|
@ -542,7 +542,7 @@ export class GoogleSlides implements INodeType {
|
|||
return {
|
||||
replaceAllText: {
|
||||
replaceText: text.replaceText,
|
||||
pageObjectIds: text.pageObjectIds ?? [],
|
||||
pageObjectIds: text.pageObjectIds || [],
|
||||
containsText: {
|
||||
text: text.text,
|
||||
matchCase: text.matchCase,
|
||||
|
|
|
@ -20,7 +20,7 @@ export async function googleApiRequest(
|
|||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: uri ?? `https://www.googleapis.com${resource}`,
|
||||
uri: uri || `https://www.googleapis.com${resource}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@ export async function googleApiRequest(
|
|||
method,
|
||||
body,
|
||||
qs,
|
||||
uri: uri ?? `https://translation.googleapis.com${resource}`,
|
||||
uri: uri || `https://translation.googleapis.com${resource}`,
|
||||
json: true,
|
||||
};
|
||||
try {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue