refactor(core): Avoid passing around static state like default timezone (no-changelog) (#7221)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-10-27 14:17:52 +02:00 committed by GitHub
parent 62c096710f
commit 35bb42c1b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 76 additions and 224 deletions

View file

@ -37,8 +37,6 @@ export abstract class AbstractServer {
protected sslCert: string; protected sslCert: string;
protected timezone: string;
protected restEndpoint: string; protected restEndpoint: string;
protected endpointWebhook: string; protected endpointWebhook: string;
@ -61,8 +59,6 @@ export abstract class AbstractServer {
this.sslKey = config.getEnv('ssl_key'); this.sslKey = config.getEnv('ssl_key');
this.sslCert = config.getEnv('ssl_cert'); this.sslCert = config.getEnv('ssl_cert');
this.timezone = config.getEnv('generic.timezone');
this.restEndpoint = config.getEnv('endpoints.rest'); this.restEndpoint = config.getEnv('endpoints.rest');
this.endpointWebhook = config.getEnv('endpoints.webhook'); this.endpointWebhook = config.getEnv('endpoints.webhook');
this.endpointWebhookTest = config.getEnv('endpoints.webhookTest'); this.endpointWebhookTest = config.getEnv('endpoints.webhookTest');

View file

@ -106,7 +106,6 @@ export class CredentialsHelper extends ICredentialsHelper {
incomingRequestOptions: IHttpRequestOptions | IRequestOptionsSimplified, incomingRequestOptions: IHttpRequestOptions | IRequestOptionsSimplified,
workflow: Workflow, workflow: Workflow,
node: INode, node: INode,
defaultTimezone: string,
): Promise<IHttpRequestOptions> { ): Promise<IHttpRequestOptions> {
const requestOptions = incomingRequestOptions; const requestOptions = incomingRequestOptions;
const credentialType = this.credentialTypes.getByName(typeName); const credentialType = this.credentialTypes.getByName(typeName);
@ -131,20 +130,13 @@ export class CredentialsHelper extends ICredentialsHelper {
if (authenticate.type === 'generic') { if (authenticate.type === 'generic') {
Object.entries(authenticate.properties).forEach(([outerKey, outerValue]) => { Object.entries(authenticate.properties).forEach(([outerKey, outerValue]) => {
Object.entries(outerValue).forEach(([key, value]) => { Object.entries(outerValue).forEach(([key, value]) => {
keyResolved = this.resolveValue( keyResolved = this.resolveValue(key, { $credentials: credentials }, workflow, node);
key,
{ $credentials: credentials },
workflow,
node,
defaultTimezone,
);
valueResolved = this.resolveValue( valueResolved = this.resolveValue(
value as string, value as string,
{ $credentials: credentials }, { $credentials: credentials },
workflow, workflow,
node, node,
defaultTimezone,
); );
// @ts-ignore // @ts-ignore
@ -226,7 +218,6 @@ export class CredentialsHelper extends ICredentialsHelper {
additionalKeys: IWorkflowDataProxyAdditionalKeys, additionalKeys: IWorkflowDataProxyAdditionalKeys,
workflow: Workflow, workflow: Workflow,
node: INode, node: INode,
defaultTimezone: string,
): string { ): string {
if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') { if (typeof parameterValue !== 'string' || parameterValue.charAt(0) !== '=') {
return parameterValue; return parameterValue;
@ -236,7 +227,6 @@ export class CredentialsHelper extends ICredentialsHelper {
node, node,
parameterValue, parameterValue,
'internal', 'internal',
defaultTimezone,
additionalKeys, additionalKeys,
undefined, undefined,
'', '',
@ -347,7 +337,6 @@ export class CredentialsHelper extends ICredentialsHelper {
nodeCredentials: INodeCredentialsDetails, nodeCredentials: INodeCredentialsDetails,
type: string, type: string,
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
defaultTimezone: string,
raw?: boolean, raw?: boolean,
expressionResolveValues?: ICredentialsExpressionResolveValues, expressionResolveValues?: ICredentialsExpressionResolveValues,
): Promise<ICredentialDataDecryptedObject> { ): Promise<ICredentialDataDecryptedObject> {
@ -367,7 +356,6 @@ export class CredentialsHelper extends ICredentialsHelper {
decryptedDataOriginal, decryptedDataOriginal,
type, type,
mode, mode,
defaultTimezone,
expressionResolveValues, expressionResolveValues,
canUseSecrets, canUseSecrets,
); );
@ -381,7 +369,6 @@ export class CredentialsHelper extends ICredentialsHelper {
decryptedDataOriginal: ICredentialDataDecryptedObject, decryptedDataOriginal: ICredentialDataDecryptedObject,
type: string, type: string,
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
defaultTimezone: string,
expressionResolveValues?: ICredentialsExpressionResolveValues, expressionResolveValues?: ICredentialsExpressionResolveValues,
canUseSecrets?: boolean, canUseSecrets?: boolean,
): ICredentialDataDecryptedObject { ): ICredentialDataDecryptedObject {
@ -413,8 +400,6 @@ export class CredentialsHelper extends ICredentialsHelper {
}); });
if (expressionResolveValues) { if (expressionResolveValues) {
const timezone = expressionResolveValues.workflow.settings.timezone ?? defaultTimezone;
try { try {
decryptedData = expressionResolveValues.workflow.expression.getParameterValue( decryptedData = expressionResolveValues.workflow.expression.getParameterValue(
decryptedData as INodeParameters, decryptedData as INodeParameters,
@ -424,7 +409,6 @@ export class CredentialsHelper extends ICredentialsHelper {
expressionResolveValues.node.name, expressionResolveValues.node.name,
expressionResolveValues.connectionInputData, expressionResolveValues.connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
undefined, undefined,
false, false,
@ -447,7 +431,6 @@ export class CredentialsHelper extends ICredentialsHelper {
mockNode, mockNode,
decryptedData as INodeParameters, decryptedData as INodeParameters,
mode, mode,
defaultTimezone,
additionalKeys, additionalKeys,
undefined, undefined,
undefined, undefined,
@ -597,7 +580,6 @@ export class CredentialsHelper extends ICredentialsHelper {
credentialsDecrypted.data, credentialsDecrypted.data,
credentialType, credentialType,
'internal' as WorkflowExecuteMode, 'internal' as WorkflowExecuteMode,
additionalData.timezone,
undefined, undefined,
user.isOwner, user.isOwner,
); );

View file

@ -727,14 +727,12 @@ export class Server extends AbstractServer {
const additionalData = await WorkflowExecuteAdditionalData.getBase(req.user.id); const additionalData = await WorkflowExecuteAdditionalData.getBase(req.user.id);
const mode: WorkflowExecuteMode = 'internal'; const mode: WorkflowExecuteMode = 'internal';
const timezone = config.getEnv('generic.timezone');
const credentialsHelper = Container.get(CredentialsHelper); const credentialsHelper = Container.get(CredentialsHelper);
const decryptedDataOriginal = await credentialsHelper.getDecrypted( const decryptedDataOriginal = await credentialsHelper.getDecrypted(
additionalData, additionalData,
credential as INodeCredentialsDetails, credential as INodeCredentialsDetails,
credential.type, credential.type,
mode, mode,
timezone,
true, true,
); );
@ -743,7 +741,6 @@ export class Server extends AbstractServer {
decryptedDataOriginal, decryptedDataOriginal,
credential.type, credential.type,
mode, mode,
timezone,
); );
const signatureMethod = oauthCredentials.signatureMethod as string; const signatureMethod = oauthCredentials.signatureMethod as string;
@ -870,14 +867,12 @@ export class Server extends AbstractServer {
const additionalData = await WorkflowExecuteAdditionalData.getBase(req.user.id); const additionalData = await WorkflowExecuteAdditionalData.getBase(req.user.id);
const mode: WorkflowExecuteMode = 'internal'; const mode: WorkflowExecuteMode = 'internal';
const timezone = config.getEnv('generic.timezone');
const credentialsHelper = Container.get(CredentialsHelper); const credentialsHelper = Container.get(CredentialsHelper);
const decryptedDataOriginal = await credentialsHelper.getDecrypted( const decryptedDataOriginal = await credentialsHelper.getDecrypted(
additionalData, additionalData,
credential as INodeCredentialsDetails, credential as INodeCredentialsDetails,
credential.type, credential.type,
mode, mode,
timezone,
true, true,
); );
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(
@ -885,7 +880,6 @@ export class Server extends AbstractServer {
decryptedDataOriginal, decryptedDataOriginal,
credential.type, credential.type,
mode, mode,
timezone,
); );
const options: AxiosRequestConfig = { const options: AxiosRequestConfig = {

View file

@ -246,7 +246,6 @@ export async function executeWebhook(
workflowStartNode, workflowStartNode,
webhookData.webhookDescription.responseMode, webhookData.webhookDescription.responseMode,
executionMode, executionMode,
additionalData.timezone,
additionalKeys, additionalKeys,
undefined, undefined,
'onReceived', 'onReceived',
@ -255,7 +254,6 @@ export async function executeWebhook(
workflowStartNode, workflowStartNode,
webhookData.webhookDescription.responseCode, webhookData.webhookDescription.responseCode,
executionMode, executionMode,
additionalData.timezone,
additionalKeys, additionalKeys,
undefined, undefined,
200, 200,
@ -265,7 +263,6 @@ export async function executeWebhook(
workflowStartNode, workflowStartNode,
webhookData.webhookDescription.responseData, webhookData.webhookDescription.responseData,
executionMode, executionMode,
additionalData.timezone,
additionalKeys, additionalKeys,
undefined, undefined,
'firstEntryJson', 'firstEntryJson',
@ -288,7 +285,6 @@ export async function executeWebhook(
workflowStartNode, workflowStartNode,
'={{$parameter["options"]["binaryData"]}}', '={{$parameter["options"]["binaryData"]}}',
executionMode, executionMode,
additionalData.timezone,
additionalKeys, additionalKeys,
undefined, undefined,
false, false,
@ -370,7 +366,6 @@ export async function executeWebhook(
workflowStartNode, workflowStartNode,
webhookData.webhookDescription.responseHeaders, webhookData.webhookDescription.responseHeaders,
executionMode, executionMode,
additionalData.timezone,
additionalKeys, additionalKeys,
undefined, undefined,
undefined, undefined,
@ -644,7 +639,6 @@ export async function executeWebhook(
workflowStartNode, workflowStartNode,
webhookData.webhookDescription.responsePropertyName, webhookData.webhookDescription.responsePropertyName,
executionMode, executionMode,
additionalData.timezone,
additionalKeys, additionalKeys,
undefined, undefined,
undefined, undefined,
@ -658,7 +652,6 @@ export async function executeWebhook(
workflowStartNode, workflowStartNode,
webhookData.webhookDescription.responseContentType, webhookData.webhookDescription.responseContentType,
executionMode, executionMode,
additionalData.timezone,
additionalKeys, additionalKeys,
undefined, undefined,
undefined, undefined,
@ -704,7 +697,6 @@ export async function executeWebhook(
workflowStartNode, workflowStartNode,
webhookData.webhookDescription.responseBinaryPropertyName, webhookData.webhookDescription.responseBinaryPropertyName,
executionMode, executionMode,
additionalData.timezone,
additionalKeys, additionalKeys,
undefined, undefined,
'data', 'data',

View file

@ -1006,7 +1006,6 @@ export function sendDataToUI(type: string, data: IDataObject | IDataObject[]) {
/** /**
* Returns the base additional data without webhooks * Returns the base additional data without webhooks
*
*/ */
export async function getBase( export async function getBase(
userId: string, userId: string,
@ -1015,7 +1014,6 @@ export async function getBase(
): Promise<IWorkflowExecuteAdditionalData> { ): Promise<IWorkflowExecuteAdditionalData> {
const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl(); const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl();
const timezone = config.getEnv('generic.timezone');
const webhookBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhook'); const webhookBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhook');
const webhookWaitingBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhookWaiting'); const webhookWaitingBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhookWaiting');
const webhookTestBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhookTest'); const webhookTestBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhookTest');
@ -1026,7 +1024,6 @@ export async function getBase(
credentialsHelper: Container.get(CredentialsHelper), credentialsHelper: Container.get(CredentialsHelper),
executeWorkflow, executeWorkflow,
restApiUrl: urlBaseWebhook + config.getEnv('endpoints.rest'), restApiUrl: urlBaseWebhook + config.getEnv('endpoints.rest'),
timezone,
instanceBaseUrl: urlBaseWebhook, instanceBaseUrl: urlBaseWebhook,
webhookBaseUrl, webhookBaseUrl,
webhookWaitingBaseUrl, webhookWaitingBaseUrl,

View file

@ -3,6 +3,7 @@ import dotenv from 'dotenv';
import { tmpdir } from 'os'; import { tmpdir } from 'os';
import { mkdirSync, mkdtempSync, readFileSync } from 'fs'; import { mkdirSync, mkdtempSync, readFileSync } from 'fs';
import { join } from 'path'; import { join } from 'path';
import { setGlobalState } from 'n8n-workflow';
import { schema } from './schema'; import { schema } from './schema';
import { inTest, inE2ETests } from '@/constants'; import { inTest, inE2ETests } from '@/constants';
@ -73,6 +74,10 @@ config.validate({
allowed: 'strict', allowed: 'strict',
}); });
setGlobalState({
defaultTimezone: config.getEnv('generic.timezone'),
});
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
export default config; export default config;
export type Config = typeof config; export type Config = typeof config;

View file

@ -65,14 +65,12 @@ oauth2CredentialController.get(
const credentialType = credential.type; const credentialType = credential.type;
const mode: WorkflowExecuteMode = 'internal'; const mode: WorkflowExecuteMode = 'internal';
const timezone = config.getEnv('generic.timezone');
const credentialsHelper = Container.get(CredentialsHelper); const credentialsHelper = Container.get(CredentialsHelper);
const decryptedDataOriginal = await credentialsHelper.getDecrypted( const decryptedDataOriginal = await credentialsHelper.getDecrypted(
additionalData, additionalData,
credential as INodeCredentialsDetails, credential as INodeCredentialsDetails,
credentialType, credentialType,
mode, mode,
timezone,
true, true,
); );
@ -93,7 +91,6 @@ oauth2CredentialController.get(
decryptedDataOriginal, decryptedDataOriginal,
credentialType, credentialType,
mode, mode,
timezone,
); );
const token = new Csrf(); const token = new Csrf();
@ -208,14 +205,12 @@ oauth2CredentialController.get(
const additionalData = await WorkflowExecuteAdditionalData.getBase(state.cid); const additionalData = await WorkflowExecuteAdditionalData.getBase(state.cid);
const mode: WorkflowExecuteMode = 'internal'; const mode: WorkflowExecuteMode = 'internal';
const timezone = config.getEnv('generic.timezone');
const credentialsHelper = Container.get(CredentialsHelper); const credentialsHelper = Container.get(CredentialsHelper);
const decryptedDataOriginal = await credentialsHelper.getDecrypted( const decryptedDataOriginal = await credentialsHelper.getDecrypted(
additionalData, additionalData,
credential as INodeCredentialsDetails, credential as INodeCredentialsDetails,
credential.type, credential.type,
mode, mode,
timezone,
true, true,
); );
const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites( const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(
@ -223,7 +218,6 @@ oauth2CredentialController.get(
decryptedDataOriginal, decryptedDataOriginal,
credential.type, credential.type,
mode, mode,
timezone,
); );
const token = new Csrf(); const token = new Csrf();

View file

@ -18,7 +18,6 @@ import type {
} from 'n8n-workflow'; } from 'n8n-workflow';
import { CredentialsHelper } from '@/CredentialsHelper'; import { CredentialsHelper } from '@/CredentialsHelper';
import { Agent as HTTPSAgent } from 'https'; import { Agent as HTTPSAgent } from 'https';
import config from '@/config';
import { isLogStreamingEnabled } from '../MessageEventBus/MessageEventBusHelper'; import { isLogStreamingEnabled } from '../MessageEventBus/MessageEventBusHelper';
import { eventMessageGenericDestinationTestEvent } from '../EventMessageClasses/EventMessageGeneric'; import { eventMessageGenericDestinationTestEvent } from '../EventMessageClasses/EventMessageGeneric';
import { MessageEventBus } from '../MessageEventBus/MessageEventBus'; import { MessageEventBus } from '../MessageEventBus/MessageEventBus';
@ -107,13 +106,11 @@ export class MessageEventBusDestinationWebhook
async matchDecryptedCredentialType(credentialType: string) { async matchDecryptedCredentialType(credentialType: string) {
const foundCredential = Object.entries(this.credentials).find((e) => e[0] === credentialType); const foundCredential = Object.entries(this.credentials).find((e) => e[0] === credentialType);
if (foundCredential) { if (foundCredential) {
const timezone = config.getEnv('generic.timezone');
const credentialsDecrypted = await this.credentialsHelper?.getDecrypted( const credentialsDecrypted = await this.credentialsHelper?.getDecrypted(
{ secretsHelpers: SecretsHelpers } as unknown as IWorkflowExecuteAdditionalData, { secretsHelpers: SecretsHelpers } as unknown as IWorkflowExecuteAdditionalData,
foundCredential[1], foundCredential[1],
foundCredential[0], foundCredential[0],
'internal', 'internal',
timezone,
true, true,
); );
return credentialsDecrypted; return credentialsDecrypted;

View file

@ -267,8 +267,6 @@ describe('CredentialsHelper', () => {
nodeTypes, nodeTypes,
}); });
const timezone = 'America/New_York';
for (const testData of tests) { for (const testData of tests) {
test(testData.description, async () => { test(testData.description, async () => {
mockNodesAndCredentials.loadedCredentials = { mockNodesAndCredentials.loadedCredentials = {
@ -286,7 +284,6 @@ describe('CredentialsHelper', () => {
deepCopy(incomingRequestOptions), deepCopy(incomingRequestOptions),
workflow, workflow,
node, node,
timezone,
); );
expect(result).toEqual(testData.output); expect(result).toEqual(testData.output);

View file

@ -105,6 +105,7 @@ import {
createDeferredPromise, createDeferredPromise,
deepCopy, deepCopy,
fileTypeFromMimeType, fileTypeFromMimeType,
getGlobalState,
isObjectEmpty, isObjectEmpty,
isResourceMapperValue, isResourceMapperValue,
validateFieldType, validateFieldType,
@ -131,7 +132,7 @@ import {
UM_EMAIL_TEMPLATES_PWRESET, UM_EMAIL_TEMPLATES_PWRESET,
} from './Constants'; } from './Constants';
import { extractValue } from './ExtractValue'; import { extractValue } from './ExtractValue';
import type { ExtendedValidationResult, IResponseError, IWorkflowSettings } from './Interfaces'; import type { ExtendedValidationResult, IResponseError } from './Interfaces';
import { getClientCredentialsToken } from './OAuth2Helper'; import { getClientCredentialsToken } from './OAuth2Helper';
import { import {
getAllWorkflowExecutionMetadata, getAllWorkflowExecutionMetadata,
@ -1544,7 +1545,6 @@ export async function httpRequestWithAuthentication(
requestOptions, requestOptions,
workflow, workflow,
node, node,
additionalData.timezone,
); );
return await httpRequest(requestOptions); return await httpRequest(requestOptions);
} catch (error) { } catch (error) {
@ -1577,7 +1577,6 @@ export async function httpRequestWithAuthentication(
requestOptions, requestOptions,
workflow, workflow,
node, node,
additionalData.timezone,
); );
} }
// retry the request // retry the request
@ -1740,7 +1739,6 @@ export async function requestWithAuthentication(
requestOptions as IHttpRequestOptions, requestOptions as IHttpRequestOptions,
workflow, workflow,
node, node,
additionalData.timezone,
); );
return await proxyRequestToAxios(workflow, additionalData, node, requestOptions as IDataObject); return await proxyRequestToAxios(workflow, additionalData, node, requestOptions as IDataObject);
} catch (error) { } catch (error) {
@ -1765,7 +1763,6 @@ export async function requestWithAuthentication(
requestOptions as IHttpRequestOptions, requestOptions as IHttpRequestOptions,
workflow, workflow,
node, node,
additionalData.timezone,
); );
// retry the request // retry the request
return await proxyRequestToAxios( return await proxyRequestToAxios(
@ -1972,7 +1969,6 @@ export async function getCredentials(
nodeCredentials, nodeCredentials,
type, type,
mode, mode,
additionalData.timezone,
false, false,
expressionResolveValues, expressionResolveValues,
); );
@ -2222,7 +2218,6 @@ export function getNodeParameter(
parameterName: string, parameterName: string,
itemIndex: number, itemIndex: number,
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
timezone: string,
additionalKeys: IWorkflowDataProxyAdditionalKeys, additionalKeys: IWorkflowDataProxyAdditionalKeys,
executeData?: IExecuteData, executeData?: IExecuteData,
fallbackValue?: any, fallbackValue?: any,
@ -2253,7 +2248,6 @@ export function getNodeParameter(
node.name, node.name,
connectionInputData, connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
executeData, executeData,
false, false,
@ -2308,7 +2302,6 @@ export function getNodeWebhookUrl(
node: INode, node: INode,
additionalData: IWorkflowExecuteAdditionalData, additionalData: IWorkflowExecuteAdditionalData,
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
timezone: string,
additionalKeys: IWorkflowDataProxyAdditionalKeys, additionalKeys: IWorkflowDataProxyAdditionalKeys,
isTest?: boolean, isTest?: boolean,
): string | undefined { ): string | undefined {
@ -2327,7 +2320,6 @@ export function getNodeWebhookUrl(
node, node,
webhookDescription.path, webhookDescription.path,
mode, mode,
timezone,
additionalKeys, additionalKeys,
); );
if (path === undefined) { if (path === undefined) {
@ -2338,7 +2330,6 @@ export function getNodeWebhookUrl(
node, node,
webhookDescription.isFullPath, webhookDescription.isFullPath,
mode, mode,
timezone,
additionalKeys, additionalKeys,
undefined, undefined,
false, false,
@ -2348,17 +2339,9 @@ export function getNodeWebhookUrl(
/** /**
* Returns the timezone for the workflow * Returns the timezone for the workflow
*
*/ */
export function getTimezone( export function getTimezone(workflow: Workflow): string {
workflow: Workflow, return workflow.settings.timezone ?? getGlobalState().defaultTimezone;
additionalData: IWorkflowExecuteAdditionalData,
): string {
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
if (workflow.settings !== undefined && workflow.settings.timezone !== undefined) {
return (workflow.settings as IWorkflowSettings).timezone as string;
}
return additionalData.timezone;
} }
/** /**
@ -2508,7 +2491,7 @@ const getCommonWorkflowFunctions = (
getRestApiUrl: () => additionalData.restApiUrl, getRestApiUrl: () => additionalData.restApiUrl,
getInstanceBaseUrl: () => additionalData.instanceBaseUrl, getInstanceBaseUrl: () => additionalData.instanceBaseUrl,
getInstanceId: () => Container.get(InstanceSettings).instanceId, getInstanceId: () => Container.get(InstanceSettings).instanceId,
getTimezone: () => getTimezone(workflow, additionalData), getTimezone: () => getTimezone(workflow),
prepareOutputData: async (outputData) => [outputData], prepareOutputData: async (outputData) => [outputData],
}); });
@ -2772,7 +2755,6 @@ export function getExecutePollFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
undefined, undefined,
fallbackValue, fallbackValue,
@ -2831,7 +2813,6 @@ export function getExecuteTriggerFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
undefined, undefined,
fallbackValue, fallbackValue,
@ -2890,7 +2871,6 @@ export function getExecuteFunctions(
node.name, node.name,
connectionInputData, connectionInputData,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
executeData, executeData,
); );
@ -2983,7 +2963,6 @@ export function getExecuteFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
executeData, executeData,
fallbackValue, fallbackValue,
@ -3142,7 +3121,6 @@ export function getExecuteFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
executeData, executeData,
fallbackValue, fallbackValue,
@ -3159,7 +3137,6 @@ export function getExecuteFunctions(
connectionInputData, connectionInputData,
{}, {},
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
executeData, executeData,
); );
@ -3306,7 +3283,6 @@ export function getExecuteSingleFunctions(
node.name, node.name,
connectionInputData, connectionInputData,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
executeData, executeData,
); );
@ -3377,7 +3353,6 @@ export function getExecuteSingleFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
executeData, executeData,
fallbackValue, fallbackValue,
@ -3394,7 +3369,6 @@ export function getExecuteSingleFunctions(
connectionInputData, connectionInputData,
{}, {},
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
executeData, executeData,
); );
@ -3489,7 +3463,6 @@ export function getLoadOptionsFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
undefined, undefined,
fallbackValue, fallbackValue,
@ -3538,7 +3511,6 @@ export function getExecuteHookFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, runExecutionData), getAdditionalKeys(additionalData, mode, runExecutionData),
undefined, undefined,
fallbackValue, fallbackValue,
@ -3552,7 +3524,6 @@ export function getExecuteHookFunctions(
node, node,
additionalData, additionalData,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, null), getAdditionalKeys(additionalData, mode, null),
isTest, isTest,
); );
@ -3615,7 +3586,6 @@ export function getExecuteWebhookFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, null), getAdditionalKeys(additionalData, mode, null),
undefined, undefined,
fallbackValue, fallbackValue,
@ -3653,7 +3623,6 @@ export function getExecuteWebhookFunctions(
node, node,
additionalData, additionalData,
mode, mode,
additionalData.timezone,
getAdditionalKeys(additionalData, mode, null), getAdditionalKeys(additionalData, mode, null),
), ),
getWebhookName: () => webhookData.webhookDescription.name, getWebhookName: () => webhookData.webhookDescription.name,

View file

@ -1402,7 +1402,6 @@ export class WorkflowExecute {
checkNode, checkNode,
requiredInputs, requiredInputs,
this.mode, this.mode,
this.additionalData.timezone,
{ $version: checkNode.typeVersion }, { $version: checkNode.typeVersion },
undefined, undefined,
[], [],

View file

@ -135,7 +135,6 @@ export function WorkflowExecuteAdditionalData(
executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo) => {}, executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo) => {},
sendDataToUI: (message: string) => {}, sendDataToUI: (message: string) => {},
restApiUrl: '', restApiUrl: '',
timezone: 'America/New_York',
webhookBaseUrl: 'webhook', webhookBaseUrl: 'webhook',
webhookWaitingBaseUrl: 'webhook-waiting', webhookWaitingBaseUrl: 'webhook-waiting',
webhookTestBaseUrl: 'webhook-test', webhookTestBaseUrl: 'webhook-test',

View file

@ -515,7 +515,6 @@ export default defineComponent({
connectionInputData, connectionInputData,
{}, {},
'manual', 'manual',
this.rootStore.timezone,
additionalKeys, additionalKeys,
); );
const proxy = dataProxy.getDataProxy(); const proxy = dataProxy.getDataProxy();

View file

@ -646,7 +646,6 @@ export const nodeHelpers = defineComponent({
data as INode, data as INode,
nodeType.subtitle, nodeType.subtitle,
'internal', 'internal',
this.rootStore.timezone,
{}, {},
undefined, undefined,
PLACEHOLDER_FILLED_AT_EXECUTION_TIME, PLACEHOLDER_FILLED_AT_EXECUTION_TIME,

View file

@ -216,7 +216,6 @@ export function resolveParameter(
activeNode!.name, activeNode!.name,
_connectionInputData, _connectionInputData,
'manual', 'manual',
useRootStore().timezone,
additionalKeys, additionalKeys,
_executeData, _executeData,
false, false,

View file

@ -304,7 +304,6 @@ const dataProxy = new WorkflowDataProxy(
lastNodeConnectionInputData || [], lastNodeConnectionInputData || [],
{}, {},
'manual', 'manual',
'America/New_York',
{}, {},
executeData, executeData,
); );

View file

@ -1,6 +1,6 @@
import { CLOUD_BASE_URL_PRODUCTION, CLOUD_BASE_URL_STAGING, STORES } from '@/constants'; import { CLOUD_BASE_URL_PRODUCTION, CLOUD_BASE_URL_STAGING, STORES } from '@/constants';
import type { IRestApiContext, RootState } from '@/Interface'; import type { IRestApiContext, RootState } from '@/Interface';
import type { IDataObject } from 'n8n-workflow'; import { setGlobalState, type IDataObject } from 'n8n-workflow';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
const { VUE_APP_URL_BASE_API } = import.meta.env; const { VUE_APP_URL_BASE_API } = import.meta.env;
@ -79,6 +79,7 @@ export const useRootStore = defineStore(STORES.ROOT, {
}, },
setTimezone(timezone: string): void { setTimezone(timezone: string): void {
this.timezone = timezone; this.timezone = timezone;
setGlobalState({ defaultTimezone: timezone });
}, },
setExecutionTimeout(executionTimeout: number): void { setExecutionTimeout(executionTimeout: number): void {
this.executionTimeout = executionTimeout; this.executionTimeout = executionTimeout;

View file

@ -176,7 +176,6 @@ export function WorkflowExecuteAdditionalData(
executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo): Promise<any> => {}, executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo): Promise<any> => {},
sendDataToUI: (message: string) => {}, sendDataToUI: (message: string) => {},
restApiUrl: '', restApiUrl: '',
timezone: workflowTestData?.input.workflowData.settings?.timezone || 'America/New_York',
webhookBaseUrl: 'webhook', webhookBaseUrl: 'webhook',
webhookWaitingBaseUrl: 'webhook-waiting', webhookWaitingBaseUrl: 'webhook-waiting',
webhookTestBaseUrl: 'webhook-test', webhookTestBaseUrl: 'webhook-test',

View file

@ -23,6 +23,7 @@ import { extend, extendOptional } from './Extensions';
import { extendedFunctions } from './Extensions/ExtendedFunctions'; import { extendedFunctions } from './Extensions/ExtendedFunctions';
import { extendSyntax } from './Extensions/ExpressionExtension'; import { extendSyntax } from './Extensions/ExpressionExtension';
import { evaluateExpression, setErrorHandler } from './ExpressionEvaluatorProxy'; import { evaluateExpression, setErrorHandler } from './ExpressionEvaluatorProxy';
import { getGlobalState } from './GlobalState';
const IS_FRONTEND_IN_DEV_MODE = const IS_FRONTEND_IN_DEV_MODE =
typeof process === 'object' && typeof process === 'object' &&
@ -32,13 +33,13 @@ const IS_FRONTEND_IN_DEV_MODE =
const IS_FRONTEND = typeof process === 'undefined' || IS_FRONTEND_IN_DEV_MODE; const IS_FRONTEND = typeof process === 'undefined' || IS_FRONTEND_IN_DEV_MODE;
export const isSyntaxError = (error: unknown): error is SyntaxError => const isSyntaxError = (error: unknown): error is SyntaxError =>
error instanceof SyntaxError || (error instanceof Error && error.name === 'SyntaxError'); error instanceof SyntaxError || (error instanceof Error && error.name === 'SyntaxError');
export const isExpressionError = (error: unknown): error is ExpressionError => const isExpressionError = (error: unknown): error is ExpressionError =>
error instanceof ExpressionError || error instanceof ExpressionExtensionError; error instanceof ExpressionError || error instanceof ExpressionExtensionError;
export const isTypeError = (error: unknown): error is TypeError => const isTypeError = (error: unknown): error is TypeError =>
error instanceof TypeError || (error instanceof Error && error.name === 'TypeError'); error instanceof TypeError || (error instanceof Error && error.name === 'TypeError');
// Make sure that error get forwarded // Make sure that error get forwarded
@ -58,11 +59,7 @@ const fnConstructors = {
}; };
export class Expression { export class Expression {
workflow: Workflow; constructor(private readonly workflow: Workflow) {}
constructor(workflow: Workflow) {
this.workflow = workflow;
}
static resolveWithoutWorkflow(expression: string, data: IDataObject = {}) { static resolveWithoutWorkflow(expression: string, data: IDataObject = {}) {
return tmpl.tmpl(expression, data); return tmpl.tmpl(expression, data);
@ -84,7 +81,7 @@ export class Expression {
if (value instanceof Date) { if (value instanceof Date) {
// We don't want to use JSON.stringify for dates since it disregards workflow timezone // We don't want to use JSON.stringify for dates since it disregards workflow timezone
result = DateTime.fromJSDate(value, { result = DateTime.fromJSDate(value, {
zone: this.workflow.settings?.timezone ?? 'default', zone: this.workflow.settings?.timezone ?? getGlobalState().defaultTimezone,
}).toISO(); }).toISO();
} else { } else {
result = JSON.stringify(value); result = JSON.stringify(value);
@ -114,7 +111,6 @@ export class Expression {
activeNodeName: string, activeNodeName: string,
connectionInputData: INodeExecutionData[], connectionInputData: INodeExecutionData[],
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
timezone: string,
additionalKeys: IWorkflowDataProxyAdditionalKeys, additionalKeys: IWorkflowDataProxyAdditionalKeys,
executeData?: IExecuteData, executeData?: IExecuteData,
returnObjectAsString = false, returnObjectAsString = false,
@ -143,7 +139,6 @@ export class Expression {
connectionInputData, connectionInputData,
siblingParameters, siblingParameters,
mode, mode,
timezone,
additionalKeys, additionalKeys,
executeData, executeData,
-1, -1,
@ -371,7 +366,6 @@ export class Expression {
node: INode, node: INode,
parameterValue: string | boolean | undefined, parameterValue: string | boolean | undefined,
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
timezone: string,
additionalKeys: IWorkflowDataProxyAdditionalKeys, additionalKeys: IWorkflowDataProxyAdditionalKeys,
executeData?: IExecuteData, executeData?: IExecuteData,
defaultValue?: boolean | number | string | unknown[], defaultValue?: boolean | number | string | unknown[],
@ -399,7 +393,6 @@ export class Expression {
node.name, node.name,
connectionInputData, connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
executeData, executeData,
) as boolean | number | string | undefined; ) as boolean | number | string | undefined;
@ -415,7 +408,6 @@ export class Expression {
node: INode, node: INode,
parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[], parameterValue: NodeParameterValue | INodeParameters | NodeParameterValue[] | INodeParameters[],
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
timezone: string,
additionalKeys: IWorkflowDataProxyAdditionalKeys, additionalKeys: IWorkflowDataProxyAdditionalKeys,
executeData?: IExecuteData, executeData?: IExecuteData,
defaultValue: NodeParameterValueType | undefined = undefined, defaultValue: NodeParameterValueType | undefined = undefined,
@ -445,7 +437,6 @@ export class Expression {
node.name, node.name,
connectionInputData, connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
executeData, executeData,
false, false,
@ -461,7 +452,6 @@ export class Expression {
node.name, node.name,
connectionInputData, connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
executeData, executeData,
false, false,
@ -487,7 +477,6 @@ export class Expression {
activeNodeName: string, activeNodeName: string,
connectionInputData: INodeExecutionData[], connectionInputData: INodeExecutionData[],
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
timezone: string,
additionalKeys: IWorkflowDataProxyAdditionalKeys, additionalKeys: IWorkflowDataProxyAdditionalKeys,
executeData?: IExecuteData, executeData?: IExecuteData,
returnObjectAsString = false, returnObjectAsString = false,
@ -513,7 +502,6 @@ export class Expression {
activeNodeName, activeNodeName,
connectionInputData, connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
executeData, executeData,
returnObjectAsString, returnObjectAsString,
@ -531,7 +519,6 @@ export class Expression {
activeNodeName, activeNodeName,
connectionInputData, connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
executeData, executeData,
returnObjectAsString, returnObjectAsString,
@ -551,7 +538,6 @@ export class Expression {
activeNodeName, activeNodeName,
connectionInputData, connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
executeData, executeData,
returnObjectAsString, returnObjectAsString,

View file

@ -0,0 +1,15 @@
import { deepCopy } from './utils';
export interface GlobalState {
defaultTimezone: string;
}
let globalState: GlobalState = { defaultTimezone: 'America/New_York' };
export function setGlobalState(state: GlobalState) {
globalState = state;
}
export function getGlobalState() {
return deepCopy(globalState);
}

View file

@ -196,7 +196,6 @@ export abstract class ICredentialsHelper {
requestOptions: IHttpRequestOptions | IRequestOptionsSimplified, requestOptions: IHttpRequestOptions | IRequestOptionsSimplified,
workflow: Workflow, workflow: Workflow,
node: INode, node: INode,
defaultTimezone: string,
): Promise<IHttpRequestOptions>; ): Promise<IHttpRequestOptions>;
abstract preAuthentication( abstract preAuthentication(
@ -217,7 +216,6 @@ export abstract class ICredentialsHelper {
nodeCredentials: INodeCredentialsDetails, nodeCredentials: INodeCredentialsDetails,
type: string, type: string,
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
defaultTimezone: string,
raw?: boolean, raw?: boolean,
expressionResolveValues?: ICredentialsExpressionResolveValues, expressionResolveValues?: ICredentialsExpressionResolveValues,
): Promise<ICredentialDataDecryptedObject>; ): Promise<ICredentialDataDecryptedObject>;
@ -1862,7 +1860,6 @@ export interface IWorkflowExecuteAdditionalData {
instanceBaseUrl: string; instanceBaseUrl: string;
setExecutionStatus?: (status: ExecutionStatus) => void; setExecutionStatus?: (status: ExecutionStatus) => void;
sendDataToUI?: (type: string, data: IDataObject | IDataObject[]) => void; sendDataToUI?: (type: string, data: IDataObject | IDataObject[]) => void;
timezone: string;
webhookBaseUrl: string; webhookBaseUrl: string;
webhookWaitingBaseUrl: string; webhookWaitingBaseUrl: string;
webhookTestBaseUrl: string; webhookTestBaseUrl: string;

View file

@ -885,7 +885,6 @@ export function getNodeWebhooks(
node, node,
webhookDescription.path, webhookDescription.path,
mode, mode,
additionalData.timezone,
{}, {},
); );
if (nodeWebhookPath === undefined) { if (nodeWebhookPath === undefined) {
@ -909,7 +908,6 @@ export function getNodeWebhooks(
node, node,
webhookDescription.isFullPath, webhookDescription.isFullPath,
'internal', 'internal',
additionalData.timezone,
{}, {},
undefined, undefined,
false, false,
@ -918,7 +916,6 @@ export function getNodeWebhooks(
node, node,
webhookDescription.restartWebhook, webhookDescription.restartWebhook,
'internal', 'internal',
additionalData.timezone,
{}, {},
undefined, undefined,
false, false,
@ -929,7 +926,6 @@ export function getNodeWebhooks(
node, node,
webhookDescription.httpMethod, webhookDescription.httpMethod,
mode, mode,
additionalData.timezone,
{}, {},
undefined, undefined,
'GET', 'GET',
@ -1037,7 +1033,6 @@ export function getNodeInputs(
node, node,
nodeTypeData.inputs, nodeTypeData.inputs,
'internal', 'internal',
'',
{}, {},
) || []) as ConnectionTypes[]; ) || []) as ConnectionTypes[];
} catch (e) { } catch (e) {
@ -1060,7 +1055,6 @@ export function getNodeOutputs(
node, node,
nodeTypeData.outputs, nodeTypeData.outputs,
'internal', 'internal',
'',
{}, {},
) || []) as ConnectionTypes[]; ) || []) as ConnectionTypes[];
} catch (e) { } catch (e) {

View file

@ -701,7 +701,6 @@ export class RoutingNode {
this.node.name, this.node.name,
this.connectionInputData, this.connectionInputData,
this.mode, this.mode,
this.additionalData.timezone,
additionalKeys ?? {}, additionalKeys ?? {},
executeData, executeData,
returnObjectAsString, returnObjectAsString,

View file

@ -28,6 +28,7 @@ import { ExpressionError } from './ExpressionError';
import type { Workflow } from './Workflow'; import type { Workflow } from './Workflow';
import { augmentArray, augmentObject } from './AugmentObject'; import { augmentArray, augmentObject } from './AugmentObject';
import { deepCopy } from './utils'; import { deepCopy } from './utils';
import { getGlobalState } from './GlobalState';
export function isResourceLocatorValue(value: unknown): value is INodeParameterResourceLocator { export function isResourceLocatorValue(value: unknown): value is INodeParameterResourceLocator {
return Boolean( return Boolean(
@ -48,57 +49,28 @@ const isScriptingNode = (nodeName: string, workflow: Workflow) => {
}; };
export class WorkflowDataProxy { export class WorkflowDataProxy {
private workflow: Workflow;
private runExecutionData: IRunExecutionData | null; private runExecutionData: IRunExecutionData | null;
private defaultReturnRunIndex: number;
private runIndex: number;
private itemIndex: number;
private activeNodeName: string;
private contextNodeName: string;
private connectionInputData: INodeExecutionData[]; private connectionInputData: INodeExecutionData[];
private siblingParameters: INodeParameters;
private mode: WorkflowExecuteMode;
private selfData: IDataObject;
private additionalKeys: IWorkflowDataProxyAdditionalKeys;
private executeData: IExecuteData | undefined;
private defaultTimezone: string;
private timezone: string; private timezone: string;
// TODO: Clean that up at some point and move all the options into an options object // TODO: Clean that up at some point and move all the options into an options object
constructor( constructor(
workflow: Workflow, private workflow: Workflow,
runExecutionData: IRunExecutionData | null, runExecutionData: IRunExecutionData | null,
runIndex: number, private runIndex: number,
itemIndex: number, private itemIndex: number,
activeNodeName: string, private activeNodeName: string,
connectionInputData: INodeExecutionData[], connectionInputData: INodeExecutionData[],
siblingParameters: INodeParameters, private siblingParameters: INodeParameters,
mode: WorkflowExecuteMode, private mode: WorkflowExecuteMode,
defaultTimezone: string, private additionalKeys: IWorkflowDataProxyAdditionalKeys,
additionalKeys: IWorkflowDataProxyAdditionalKeys, private executeData?: IExecuteData,
executeData?: IExecuteData, private defaultReturnRunIndex = -1,
defaultReturnRunIndex = -1, private selfData: IDataObject = {},
selfData = {}, private contextNodeName: string = activeNodeName,
contextNodeName?: string,
) { ) {
this.activeNodeName = activeNodeName;
this.contextNodeName = contextNodeName || activeNodeName;
this.workflow = workflow;
this.runExecutionData = isScriptingNode(this.contextNodeName, workflow) this.runExecutionData = isScriptingNode(this.contextNodeName, workflow)
? runExecutionData !== null ? runExecutionData !== null
? augmentObject(runExecutionData) ? augmentObject(runExecutionData)
@ -109,16 +81,7 @@ export class WorkflowDataProxy {
? augmentArray(connectionInputData) ? augmentArray(connectionInputData)
: connectionInputData; : connectionInputData;
this.defaultReturnRunIndex = defaultReturnRunIndex; this.timezone = workflow.settings?.timezone ?? getGlobalState().defaultTimezone;
this.runIndex = runIndex;
this.itemIndex = itemIndex;
this.siblingParameters = siblingParameters;
this.mode = mode;
this.defaultTimezone = defaultTimezone;
this.timezone = workflow.settings?.timezone ?? defaultTimezone;
this.selfData = selfData;
this.additionalKeys = additionalKeys;
this.executeData = executeData;
Settings.defaultZone = this.timezone; Settings.defaultZone = this.timezone;
} }
@ -266,7 +229,6 @@ export class WorkflowDataProxy {
that.activeNodeName, that.activeNodeName,
that.connectionInputData, that.connectionInputData,
that.mode, that.mode,
that.timezone,
that.additionalKeys, that.additionalKeys,
that.executeData, that.executeData,
false, false,
@ -1185,7 +1147,6 @@ export class WorkflowDataProxy {
that.activeNodeName, that.activeNodeName,
that.connectionInputData, that.connectionInputData,
that.mode, that.mode,
that.timezone,
that.additionalKeys, that.additionalKeys,
that.executeData, that.executeData,
false, false,
@ -1204,10 +1165,10 @@ export class WorkflowDataProxy {
this.connectionInputData, this.connectionInputData,
that.siblingParameters, that.siblingParameters,
that.mode, that.mode,
that.defaultTimezone,
that.additionalKeys, that.additionalKeys,
that.executeData, that.executeData,
defaultReturnRunIndex, defaultReturnRunIndex,
{},
that.contextNodeName, that.contextNodeName,
); );
return dataProxy.getDataProxy(); return dataProxy.getDataProxy();

View file

@ -9,6 +9,7 @@ export * from './Authentication';
export * from './Constants'; export * from './Constants';
export * from './Cron'; export * from './Cron';
export * from './DeferredPromise'; export * from './DeferredPromise';
export * from './GlobalState';
export * from './Interfaces'; export * from './Interfaces';
export * from './MessageEventBus'; export * from './MessageEventBus';
export * from './ExecutionStatus'; export * from './ExecutionStatus';

View file

@ -3,7 +3,6 @@
*/ */
import { DateTime, Duration, Interval } from 'luxon'; import { DateTime, Duration, Interval } from 'luxon';
import { Expression } from '@/Expression';
import { Workflow } from '@/Workflow'; import { Workflow } from '@/Workflow';
import * as Helpers from './Helpers'; import * as Helpers from './Helpers';
import type { ExpressionTestEvaluation, ExpressionTestTransform } from './ExpressionFixtures/base'; import type { ExpressionTestEvaluation, ExpressionTestTransform } from './ExpressionFixtures/base';
@ -21,6 +20,7 @@ for (const evaluator of ['tmpl', 'tournament'] as const) {
describe('getParameterValue()', () => { describe('getParameterValue()', () => {
const nodeTypes = Helpers.NodeTypes(); const nodeTypes = Helpers.NodeTypes();
const workflow = new Workflow({ const workflow = new Workflow({
id: '1',
nodes: [ nodes: [
{ {
name: 'node', name: 'node',
@ -35,10 +35,10 @@ for (const evaluator of ['tmpl', 'tournament'] as const) {
active: false, active: false,
nodeTypes, nodeTypes,
}); });
const expression = new Expression(workflow); const expression = workflow.expression;
const evaluate = (value: string) => const evaluate = (value: string) =>
expression.getParameterValue(value, null, 0, 0, 'node', [], 'manual', '', {}); expression.getParameterValue(value, null, 0, 0, 'node', [], 'manual', {});
it('should not be able to use global built-ins from denylist', () => { it('should not be able to use global built-ins from denylist', () => {
expect(evaluate('={{document}}')).toEqual({}); expect(evaluate('={{document}}')).toEqual({});
@ -84,9 +84,13 @@ for (const evaluator of ['tmpl', 'tournament'] as const) {
expect(evaluate('={{DateTime.now().toLocaleString()}}')).toEqual( expect(evaluate('={{DateTime.now().toLocaleString()}}')).toEqual(
DateTime.now().toLocaleString(), DateTime.now().toLocaleString(),
); );
jest.useFakeTimers({ now: new Date() });
expect(evaluate('={{Interval.after(new Date(), 100)}}')).toEqual( expect(evaluate('={{Interval.after(new Date(), 100)}}')).toEqual(
Interval.after(new Date(), 100), Interval.after(new Date(), 100),
); );
jest.useRealTimers();
expect(evaluate('={{Duration.fromMillis(100)}}')).toEqual(Duration.fromMillis(100)); expect(evaluate('={{Duration.fromMillis(100)}}')).toEqual(Duration.fromMillis(100));
expect(evaluate('={{new Object()}}')).toEqual(new Object()); expect(evaluate('={{new Object()}}')).toEqual(new Object());
@ -170,6 +174,7 @@ for (const evaluator of ['tmpl', 'tournament'] as const) {
describe('Test all expression value fixtures', () => { describe('Test all expression value fixtures', () => {
const nodeTypes = Helpers.NodeTypes(); const nodeTypes = Helpers.NodeTypes();
const workflow = new Workflow({ const workflow = new Workflow({
id: '1',
nodes: [ nodes: [
{ {
name: 'node', name: 'node',
@ -185,21 +190,11 @@ for (const evaluator of ['tmpl', 'tournament'] as const) {
nodeTypes, nodeTypes,
}); });
const expression = new Expression(workflow); const expression = workflow.expression;
const evaluate = (value: string, data: INodeExecutionData[]) => { const evaluate = (value: string, data: INodeExecutionData[]) => {
const itemIndex = data.length === 0 ? -1 : 0; const itemIndex = data.length === 0 ? -1 : 0;
return expression.getParameterValue( return expression.getParameterValue(value, null, 0, itemIndex, 'node', data, 'manual', {});
value,
null,
0,
itemIndex,
'node',
data,
'manual',
'',
{},
);
}; };
for (const t of baseFixtures) { for (const t of baseFixtures) {

View file

@ -3,7 +3,10 @@
*/ */
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { evaluate, getLocalISOString, TEST_TIMEZONE } from './Helpers'; import { getGlobalState } from '@/GlobalState';
import { evaluate, getLocalISOString } from './Helpers';
const { defaultTimezone } = getGlobalState();
describe('Data Transformation Functions', () => { describe('Data Transformation Functions', () => {
describe('Date Data Transformation Functions', () => { describe('Date Data Transformation Functions', () => {
@ -16,17 +19,17 @@ describe('Data Transformation Functions', () => {
test('.beginningOf("week") should work correctly on a date', () => { test('.beginningOf("week") should work correctly on a date', () => {
expect(evaluate('={{ DateTime.local(2023, 1, 20).beginningOf("week") }}')).toEqual( expect(evaluate('={{ DateTime.local(2023, 1, 20).beginningOf("week") }}')).toEqual(
DateTime.local(2023, 1, 16, { zone: TEST_TIMEZONE }), DateTime.local(2023, 1, 16, { zone: defaultTimezone }),
); );
expect(evaluate('={{ new Date(2023, 0, 20).beginningOf("week") }}')).toEqual( expect(evaluate('={{ new Date(2023, 0, 20).beginningOf("week") }}')).toEqual(
DateTime.local(2023, 1, 16, { zone: TEST_TIMEZONE }).toJSDate(), DateTime.local(2023, 1, 16, { zone: defaultTimezone }).toJSDate(),
); );
}); });
test('.beginningOf("week") should work correctly on a string', () => { test('.beginningOf("week") should work correctly on a string', () => {
const evaluatedDate = evaluate('={{ "2023-01-30".toDate().beginningOf("week") }}'); const evaluatedDate = evaluate('={{ "2023-01-30".toDate().beginningOf("week") }}');
const expectedDate = DateTime.local(2023, 1, 23, { zone: TEST_TIMEZONE }).toJSDate(); const expectedDate = DateTime.local(2023, 1, 23, { zone: defaultTimezone }).toJSDate();
if (evaluatedDate && evaluatedDate instanceof Date) { if (evaluatedDate && evaluatedDate instanceof Date) {
expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString()); expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString());
@ -35,7 +38,7 @@ describe('Data Transformation Functions', () => {
test('.beginningOf("month") should work correctly on a string', () => { test('.beginningOf("month") should work correctly on a string', () => {
const evaluatedDate = evaluate('={{ "2023-06-16".toDate().beginningOf("month") }}'); const evaluatedDate = evaluate('={{ "2023-06-16".toDate().beginningOf("month") }}');
const expectedDate = DateTime.local(2023, 6, 1, { zone: TEST_TIMEZONE }).toJSDate(); const expectedDate = DateTime.local(2023, 6, 1, { zone: defaultTimezone }).toJSDate();
if (evaluatedDate && evaluatedDate instanceof Date) { if (evaluatedDate && evaluatedDate instanceof Date) {
expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString()); expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString());
@ -44,7 +47,7 @@ describe('Data Transformation Functions', () => {
test('.beginningOf("year") should work correctly on a string', () => { test('.beginningOf("year") should work correctly on a string', () => {
const evaluatedDate = evaluate('={{ "2023-01-30".toDate().beginningOf("year") }}'); const evaluatedDate = evaluate('={{ "2023-01-30".toDate().beginningOf("year") }}');
const expectedDate = DateTime.local(2023, 1, 1, { zone: TEST_TIMEZONE }).toJSDate(); const expectedDate = DateTime.local(2023, 1, 1, { zone: defaultTimezone }).toJSDate();
if (evaluatedDate && evaluatedDate instanceof Date) { if (evaluatedDate && evaluatedDate instanceof Date) {
expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString()); expect(evaluatedDate.toDateString()).toEqual(expectedDate.toDateString());
@ -53,10 +56,10 @@ describe('Data Transformation Functions', () => {
test('.endOfMonth() should work correctly on a date', () => { test('.endOfMonth() should work correctly on a date', () => {
expect(evaluate('={{ DateTime.local(2023, 1, 16).endOfMonth() }}')).toEqual( expect(evaluate('={{ DateTime.local(2023, 1, 16).endOfMonth() }}')).toEqual(
DateTime.local(2023, 1, 31, 23, 59, 59, 999, { zone: TEST_TIMEZONE }), DateTime.local(2023, 1, 31, 23, 59, 59, 999, { zone: defaultTimezone }),
); );
expect(evaluate('={{ new Date(2023, 0, 16).endOfMonth() }}')).toEqual( expect(evaluate('={{ new Date(2023, 0, 16).endOfMonth() }}')).toEqual(
DateTime.local(2023, 1, 31, 23, 59, 59, 999, { zone: TEST_TIMEZONE }).toJSDate(), DateTime.local(2023, 1, 31, 23, 59, 59, 999, { zone: defaultTimezone }).toJSDate(),
); );
}); });

View file

@ -1,10 +1,7 @@
import type { IDataObject } from '@/Interfaces'; import type { IDataObject } from '@/Interfaces';
import { Expression } from '@/Expression';
import { Workflow } from '@/Workflow'; import { Workflow } from '@/Workflow';
import * as Helpers from '../Helpers'; import * as Helpers from '../Helpers';
export const TEST_TIMEZONE = 'America/New_York';
export const nodeTypes = Helpers.NodeTypes(); export const nodeTypes = Helpers.NodeTypes();
export const workflow = new Workflow({ export const workflow = new Workflow({
nodes: [ nodes: [
@ -20,11 +17,8 @@ export const workflow = new Workflow({
connections: {}, connections: {},
active: false, active: false,
nodeTypes, nodeTypes,
settings: {
timezone: TEST_TIMEZONE,
},
}); });
export const expression = new Expression(workflow); export const expression = workflow.expression;
export const evaluate = (value: string, values?: IDataObject[]) => export const evaluate = (value: string, values?: IDataObject[]) =>
expression.getParameterValue( expression.getParameterValue(
@ -35,7 +29,6 @@ export const evaluate = (value: string, values?: IDataObject[]) =>
'node', 'node',
values?.map((v) => ({ json: v })) ?? [], values?.map((v) => ({ json: v })) ?? [],
'manual', 'manual',
TEST_TIMEZONE,
{}, {},
); );

View file

@ -37,6 +37,8 @@ import type { Workflow } from '@/Workflow';
import { WorkflowDataProxy } from '@/WorkflowDataProxy'; import { WorkflowDataProxy } from '@/WorkflowDataProxy';
import { WorkflowHooks } from '@/WorkflowHooks'; import { WorkflowHooks } from '@/WorkflowHooks';
import * as NodeHelpers from '@/NodeHelpers'; import * as NodeHelpers from '@/NodeHelpers';
import { deepCopy } from '@/utils';
import { getGlobalState } from '@/GlobalState';
export interface INodeTypesObject { export interface INodeTypesObject {
[key: string]: INodeType; [key: string]: INodeType;
@ -127,7 +129,6 @@ export function getNodeParameter(
parameterName: string, parameterName: string,
itemIndex: number, itemIndex: number,
mode: WorkflowExecuteMode, mode: WorkflowExecuteMode,
timezone: string,
additionalKeys: IWorkflowDataProxyAdditionalKeys, additionalKeys: IWorkflowDataProxyAdditionalKeys,
executeData: IExecuteData, executeData: IExecuteData,
fallbackValue?: any, fallbackValue?: any,
@ -153,7 +154,6 @@ export function getNodeParameter(
node.name, node.name,
connectionInputData, connectionInputData,
mode, mode,
timezone,
additionalKeys, additionalKeys,
); );
} catch (e) { } catch (e) {
@ -240,7 +240,6 @@ export function getExecuteFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
{}, {},
fallbackValue, fallbackValue,
); );
@ -255,7 +254,7 @@ export function getExecuteFunctions(
return additionalData.restApiUrl; return additionalData.restApiUrl;
}, },
getTimezone: (): string => { getTimezone: (): string => {
return additionalData.timezone; return workflow.settings.timezone ?? getGlobalState().defaultTimezone;
}, },
getExecuteData: (): IExecuteData => { getExecuteData: (): IExecuteData => {
return executeData; return executeData;
@ -277,7 +276,6 @@ export function getExecuteFunctions(
connectionInputData, connectionInputData,
{}, {},
mode, mode,
additionalData.timezone,
{}, {},
executeData, executeData,
); );
@ -421,7 +419,7 @@ export function getExecuteSingleFunctions(
return additionalData.restApiUrl; return additionalData.restApiUrl;
}, },
getTimezone: (): string => { getTimezone: (): string => {
return additionalData.timezone; return workflow.settings.timezone ?? getGlobalState().defaultTimezone;
}, },
getExecuteData: (): IExecuteData => { getExecuteData: (): IExecuteData => {
return executeData; return executeData;
@ -444,7 +442,6 @@ export function getExecuteSingleFunctions(
parameterName, parameterName,
itemIndex, itemIndex,
mode, mode,
additionalData.timezone,
{}, {},
fallbackValue, fallbackValue,
); );
@ -466,7 +463,6 @@ export function getExecuteSingleFunctions(
connectionInputData, connectionInputData,
{}, {},
mode, mode,
additionalData.timezone,
{}, {},
executeData, executeData,
); );
@ -679,7 +675,6 @@ export function WorkflowExecuteAdditionalData(): IWorkflowExecuteAdditionalData
executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo): Promise<any> => {}, executeWorkflow: async (workflowInfo: IExecuteWorkflowInfo): Promise<any> => {},
sendDataToUI: (message: string) => {}, sendDataToUI: (message: string) => {},
restApiUrl: '', restApiUrl: '',
timezone: 'America/New_York',
webhookBaseUrl: 'webhook', webhookBaseUrl: 'webhook',
webhookWaitingBaseUrl: 'webhook-waiting', webhookWaitingBaseUrl: 'webhook-waiting',
webhookTestBaseUrl: 'webhook-test', webhookTestBaseUrl: 'webhook-test',

View file

@ -1182,7 +1182,6 @@ describe('Workflow', () => {
]; ];
const nodeTypes = Helpers.NodeTypes(); const nodeTypes = Helpers.NodeTypes();
const timezone = 'America/New_York';
for (const testData of tests) { for (const testData of tests) {
test(testData.description, () => { test(testData.description, () => {
@ -1310,7 +1309,6 @@ describe('Workflow', () => {
activeNodeName, activeNodeName,
connectionInputData, connectionInputData,
'manual', 'manual',
timezone,
{}, {},
); );
expect(result).toEqual(testData.output[parameterName]); expect(result).toEqual(testData.output[parameterName]);
@ -1465,7 +1463,6 @@ describe('Workflow', () => {
activeNodeName, activeNodeName,
connectionInputData, connectionInputData,
'manual', 'manual',
timezone,
{}, {},
); );

View file

@ -253,10 +253,9 @@ describe('WorkflowDataProxy', () => {
0, 0,
0, 0,
nameLastNode, nameLastNode,
lastNodeConnectionInputData || [], lastNodeConnectionInputData ?? [],
{}, {},
'manual', 'manual',
'America/New_York',
{}, {},
executeData, executeData,
); );