🚀 Release 0.222.0 (#5786)

This commit is contained in:
github-actions[bot]
2023-03-30 14:53:19 +02:00
committed by GitHub
parent dd20127961
commit e92a993694
23 changed files with 145 additions and 70 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "n8n",
"version": "0.221.2",
"version": "0.222.0",
"description": "n8n Workflow Automation Tool",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",

View File

@@ -165,7 +165,7 @@ export interface IExecutionBase {
// Data in regular format with references
export interface IExecutionDb extends IExecutionBase {
data: IRunExecutionData;
waitTill?: Date;
waitTill?: Date | null;
workflowData?: IWorkflowBase;
}
@@ -179,7 +179,7 @@ export interface IExecutionResponse extends IExecutionBase {
data: IRunExecutionData;
retryOf?: string;
retrySuccessId?: string;
waitTill?: Date;
waitTill?: Date | null;
workflowData: IWorkflowBase;
}

View File

@@ -124,7 +124,7 @@ export class WaitTracker {
};
fullExecutionData.stoppedAt = new Date();
fullExecutionData.waitTill = undefined;
fullExecutionData.waitTill = null;
fullExecutionData.status = 'canceled';
await Db.collections.Execution.update(

View File

@@ -49,7 +49,7 @@ export class ExecutionEntity implements IExecutionFlattedDb {
workflowId: string;
@Column({ type: datetimeColumnType, nullable: true })
waitTill: Date;
waitTill: Date | null;
@OneToMany('ExecutionMetadata', 'execution')
metadata: ExecutionMetadata[];

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-core",
"version": "0.160.0",
"version": "0.161.0",
"description": "Core functionality of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-design-system",
"version": "0.59.0",
"version": "0.60.0",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",
"author": {

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-editor-ui",
"version": "0.187.0",
"version": "0.188.0",
"description": "Workflow Editor UI for n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",

View File

@@ -2,7 +2,7 @@
<span>
<div class="push-connection-lost primary-color" v-if="!rootStore.pushConnectionActive">
<n8n-tooltip placement="bottom-end">
<template #current>
<template #content>
<div v-html="$locale.baseText('pushConnectionTracker.cannotConnectToServer')"></div>
</template>
<span>

View File

@@ -3885,8 +3885,8 @@ export default mixins(
this.bindCanvasEvents();
} catch {} // This will break if mounted after jsplumb has been initiated from executions preview, so continue if it breaks
await this.initView();
if (window.top) {
window.top.postMessage(
if (window.parent) {
window.parent.postMessage(
JSON.stringify({ command: 'n8nReady', version: this.rootStore.versionCli }),
'*',
);

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-node-dev",
"version": "0.99.0",
"version": "0.100.0",
"description": "CLI to simplify n8n credentials/node development",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",

View File

@@ -285,7 +285,7 @@ export class Aws implements ICredentialType {
const method = requestOptions.method;
let body = requestOptions.body;
let region = credentials.region;
const query = requestOptions.qs?.query as IDataObject;
let query = requestOptions.qs?.query as IDataObject;
// ! Workaround as we still use the OptionsWithUri interface which uses uri instead of url
// ! To change when we replace the interface with IHttpRequestOptions
@@ -295,8 +295,12 @@ export class Aws implements ICredentialType {
endpoint = new URL(requestOptions.url);
if (service === 'sts') {
try {
endpoint.searchParams.set('Action', 'GetCallerIdentity');
endpoint.searchParams.set('Version', '2011-06-15');
if (requestWithUri.qs?.Action !== 'GetCallerIdentity') {
query = requestWithUri.qs;
} else {
endpoint.searchParams.set('Action', 'GetCallerIdentity');
endpoint.searchParams.set('Version', '2011-06-15');
}
} catch (err) {
console.log(err);
}
@@ -342,6 +346,7 @@ export class Aws implements ICredentialType {
endpoint = customUrl;
}
}
if (query && Object.keys(query).length !== 0) {
Object.keys(query).forEach((key) => {
endpoint.searchParams.append(key, query[key] as string);

View File

@@ -29,6 +29,9 @@ export class GoogleApi implements ICredentialType {
description:
'Enter the private key located in the JSON file downloaded from Google Cloud Console',
required: true,
typeOptions: {
password: true,
},
},
{
displayName: 'Impersonate a User',

View File

@@ -12,9 +12,7 @@ function isTraversable(maybe: unknown): maybe is IDataObject {
* Stringify any non-standard JS objects (e.g. `Date`, `RegExp`) inside output items at any depth.
*/
export function standardizeOutput(output: IDataObject) {
const knownObjects = new WeakSet();
function standardizeOutputRecursive(obj: IDataObject): IDataObject {
function standardizeOutputRecursive(obj: IDataObject, knownObjects = new WeakSet()): IDataObject {
for (const [key, value] of Object.entries(obj)) {
if (!isTraversable(value)) continue;
@@ -29,7 +27,7 @@ export function standardizeOutput(output: IDataObject) {
obj[key] =
value.constructor.name !== 'Object'
? JSON.stringify(value) // Date, RegExp, etc.
: standardizeOutputRecursive(value);
: standardizeOutputRecursive(value, knownObjects);
}
return obj;
}

View File

@@ -536,9 +536,15 @@ export class GoogleSheet {
columnNames.indexOf(name),
);
let updateValue = item[name] as string;
if (typeof updateValue === 'object') {
try {
updateValue = JSON.stringify(updateValue);
} catch (error) {}
}
updateData.push({
range: `${decodedRange.name}!${columnToUpdate}${updateRowIndex}`,
values: [[item[name] as string]],
values: [[updateValue]],
});
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-nodes-base",
"version": "0.219.0",
"version": "0.220.0",
"description": "Base nodes of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",

View File

@@ -1,6 +1,6 @@
{
"name": "n8n-workflow",
"version": "0.142.0",
"version": "0.143.0",
"description": "Workflow base code of n8n",
"license": "SEE LICENSE IN LICENSE.md",
"homepage": "https://n8n.io",

View File

@@ -1,5 +1,26 @@
import type { IDataObject } from './Interfaces';
import util from 'util';
const augmentedObjects = new WeakSet<object>();
function augment<T>(value: T): T {
if (
typeof value !== 'object' ||
value === null ||
value instanceof RegExp ||
augmentedObjects.has(value)
)
return value;
// Track augmented objects to prevent infinite recursion in cases where an object contains circular references
augmentedObjects.add(value);
if (value instanceof Date) return new Date(value.valueOf()) as T;
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (Array.isArray(value)) return augmentArray(value) as T;
// eslint-disable-next-line @typescript-eslint/no-use-before-define
return augmentObject(value) as T;
}
export function augmentArray<T>(data: T[]): T[] {
let newData: unknown[] | undefined = undefined;
@@ -17,24 +38,12 @@ export function augmentArray<T>(data: T[]): T[] {
},
get(target, key: string, receiver): unknown {
const value = Reflect.get(newData !== undefined ? newData : target, key, receiver) as unknown;
if (typeof value === 'object') {
if (value === null || util.types.isProxy(value)) {
return value;
}
const newValue = augment(value);
if (newValue !== value) {
newData = getData();
if (Array.isArray(value)) {
Reflect.set(newData, key, augmentArray(value));
} else {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
Reflect.set(newData, key, augmentObject(value as IDataObject));
}
return Reflect.get(newData, key);
Reflect.set(newData, key, newValue);
return newValue;
}
return value;
},
getOwnPropertyDescriptor(target, key) {
@@ -83,18 +92,13 @@ export function augmentObject<T extends object>(data: T): T {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const value = Reflect.get(target, key, receiver);
if (value !== null && typeof value === 'object') {
if (Array.isArray(value)) {
newData[key] = augmentArray(value);
} else {
newData[key] = augmentObject(value as IDataObject);
}
return newData[key];
const newValue = augment(value);
if (newValue !== value) {
Object.assign(newData, { [key]: newValue });
return newValue;
}
return value as string;
return value;
},
deleteProperty(target, key: string) {
if (key in newData) {

View File

@@ -1547,7 +1547,7 @@ export interface IRun {
data: IRunExecutionData;
finished?: boolean;
mode: WorkflowExecuteMode;
waitTill?: Date;
waitTill?: Date | null;
startedAt: Date;
stoppedAt?: Date;
status: ExecutionStatus;

View File

@@ -64,27 +64,23 @@ export const jsonParse = <T>(jsonString: string, options?: JSONParseOptions<T>):
type JSONStringifyOptions = {
replaceCircularRefs?: boolean;
circularRefReplacement?: string;
};
const getReplaceCircularReferencesFn = (options: JSONStringifyOptions) => {
const knownObjects = new WeakSet();
return (key: any, value: any) => {
if (typeof value === 'object' && value !== null) {
if (knownObjects.has(value)) {
return options?.circularRefReplacement ?? '[Circular Reference]';
}
knownObjects.add(value);
}
return value;
};
const replaceCircularReferences = <T>(value: T, knownObjects = new WeakSet()): T => {
if (typeof value !== 'object' || value === null || value instanceof RegExp) return value;
if ('toJSON' in value && typeof value.toJSON === 'function') return value.toJSON() as T;
if (knownObjects.has(value)) return '[Circular Reference]' as T;
knownObjects.add(value);
const copy = (Array.isArray(value) ? [] : {}) as T;
for (const key in value) {
copy[key] = replaceCircularReferences(value[key], knownObjects);
}
knownObjects.delete(value);
return copy;
};
export const jsonStringify = (obj: unknown, options: JSONStringifyOptions = {}): string => {
const replacer = options?.replaceCircularRefs
? getReplaceCircularReferencesFn(options)
: undefined;
return JSON.stringify(obj, replacer);
return JSON.stringify(options?.replaceCircularRefs ? replaceCircularReferences(obj) : obj);
};
export const sleep = async (ms: number): Promise<void> =>

View File

@@ -193,11 +193,15 @@ describe('AugmentObject', () => {
describe('augmentObject', () => {
test('should work with simple values on first level', () => {
const date = new Date(1680089084200);
const regexp = new RegExp('^test$', 'ig');
const originalObject: IDataObject = {
1: 11,
2: '22',
a: 111,
b: '222',
d: date,
r: regexp,
};
const copyOriginal = JSON.parse(JSON.stringify(originalObject));
@@ -221,7 +225,7 @@ describe('AugmentObject', () => {
augmentedObject.c = 3;
expect(originalObject).toEqual(copyOriginal);
expect({ ...originalObject, d: date.toJSON(), r: {} }).toEqual(copyOriginal);
expect(augmentedObject).toEqual({
1: 911,
@@ -229,6 +233,8 @@ describe('AugmentObject', () => {
a: 9111,
b: '9222',
c: 3,
d: date,
r: regexp,
});
});

View File

@@ -18,7 +18,7 @@ describe('jsonParse', () => {
});
describe('jsonStringify', () => {
const source: any = { a: 1, b: 2 };
const source: any = { a: 1, b: 2, d: new Date(1680089084200), r: new RegExp('^test$', 'ig') };
source.c = source;
it('should throw errors on circular references by default', () => {
@@ -27,7 +27,15 @@ describe('jsonStringify', () => {
it('should break circular references when requested', () => {
expect(jsonStringify(source, { replaceCircularRefs: true })).toEqual(
'{"a":1,"b":2,"c":"[Circular Reference]"}',
'{"a":1,"b":2,"d":"2023-03-29T11:24:44.200Z","r":{},"c":"[Circular Reference]"}',
);
});
it('should not detect duplicates as circular references', () => {
const y = { z: 5 };
const x = [y, y, { y }];
expect(jsonStringify(x, { replaceCircularRefs: true })).toEqual(
'[{"z":5},{"z":5},{"y":{"z":5}}]',
);
});
});