Files
Automata/packages/cli/src/databases/entities/WorkflowEntity.ts
Iván Ovejero 2b5613ed68 fix(core): fix workflow hasing for MySQL (#4491)
* 🐛 Alphabetize node keys

* 🔥 Remove excess braces
2022-11-01 13:51:13 +01:00

129 lines
2.7 KiB
TypeScript

import crypto from 'crypto';
import { Length } from 'class-validator';
import type {
IBinaryKeyData,
IConnections,
IDataObject,
INode,
IPairedItemData,
IWorkflowSettings,
} from 'n8n-workflow';
import {
AfterLoad,
AfterUpdate,
AfterInsert,
Column,
Entity,
Index,
JoinTable,
ManyToMany,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
import * as config from '../../../config';
import { TagEntity } from './TagEntity';
import { SharedWorkflow } from './SharedWorkflow';
import { objectRetriever, sqlite } from '../utils/transformers';
import { AbstractEntity, jsonColumnType } from './AbstractEntity';
import type { IWorkflowDb } from '../../Interfaces';
import { alphabetizeKeys } from '../../utils';
@Entity()
export class WorkflowEntity extends AbstractEntity implements IWorkflowDb {
@PrimaryGeneratedColumn()
id: number;
// TODO: Add XSS check
@Index({ unique: true })
@Length(1, 128, {
message: 'Workflow name must be $constraint1 to $constraint2 characters long.',
})
@Column({ length: 128 })
name: string;
@Column()
active: boolean;
@Column(jsonColumnType)
nodes: INode[];
@Column(jsonColumnType)
connections: IConnections;
@Column({
type: jsonColumnType,
nullable: true,
})
settings?: IWorkflowSettings;
@Column({
type: jsonColumnType,
nullable: true,
transformer: objectRetriever,
})
staticData?: IDataObject;
@ManyToMany(() => TagEntity, (tag) => tag.workflows)
@JoinTable({
name: 'workflows_tags', // table name for the junction table of this relation
joinColumn: {
name: 'workflowId',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'tagId',
referencedColumnName: 'id',
},
})
tags?: TagEntity[];
@OneToMany(() => SharedWorkflow, (sharedWorkflow) => sharedWorkflow.workflow)
shared: SharedWorkflow[];
@Column({
type: config.getEnv('database.type') === 'sqlite' ? 'text' : 'json',
nullable: true,
transformer: sqlite.jsonColumn,
})
pinData: ISimplifiedPinData;
/**
* Hash of editable workflow state.
*/
hash: string;
@AfterLoad()
@AfterUpdate()
@AfterInsert()
setHash(): void {
const { name, active, nodes, connections, settings, staticData, pinData } = this;
const state = JSON.stringify({
name,
active,
nodes: nodes ? nodes.map(alphabetizeKeys) : [],
connections,
settings,
staticData,
pinData,
});
this.hash = crypto.createHash('md5').update(state).digest('hex');
}
}
/**
* Simplified to prevent excessively deep type instantiation error from
* `INodeExecutionData` in `IPinData` in a TypeORM entity field.
*/
export interface ISimplifiedPinData {
[nodeName: string]: Array<{
json: IDataObject;
binary?: IBinaryKeyData;
pairedItem?: IPairedItemData | IPairedItemData[] | number;
}>;
}