Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

N8N-3750 Redesign button component #3511

Merged
merged 51 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
a156dfa
feat: Design system color improvements and button component redesign.
alexgrozav May 26, 2022
6d4b9e9
feat: Added button focus state and unit tests.
alexgrozav May 27, 2022
a24a10a
refactor: Aligned n8n-button usage inside of editor-ui.
alexgrozav May 27, 2022
8eafe56
test: Updated snapshots.
alexgrozav May 27, 2022
11ef3a2
refactor: Extracted focus outline width into scss variable.
alexgrozav May 30, 2022
0c46dcb
fix: Fixed select input border-radius.
alexgrozav May 30, 2022
9c05eea
refactor: Removed element-ui references in button.
alexgrozav May 31, 2022
e548e00
fix: Fixed scss variable imports.
alexgrozav Jun 1, 2022
110f846
feat: Added color-neutral variable story.
alexgrozav Jun 2, 2022
18bd8ca
fix: Fixed color-secondary variable definition.
alexgrozav Jun 2, 2022
8977f5a
feat: Added color-white story.
alexgrozav Jun 2, 2022
3b01a3e
test: Updated button snapshot.
alexgrozav Jun 2, 2022
f6d40d0
feat: Replaced zoom buttons with new n8n-icon-button.
alexgrozav Jun 2, 2022
4226c15
feat: Added stories for float utilities.
alexgrozav Jun 6, 2022
7b0fb44
chore: Updated color shades generation code for later use.
alexgrozav Jun 6, 2022
c68c310
chore: Removed color-white code.
alexgrozav Jun 6, 2022
4dd28dc
chore: Updated story properties for button components.
alexgrozav Jun 6, 2022
9c9df2d
fix: Added el-button fallback for places where el-button is not repla…
alexgrozav Jun 6, 2022
347fecc
chore: Merged master into n8n-3750-redesign-button-component
alexgrozav Jun 14, 2022
74813ba
feat: Reverted to css modules. Replaced el-button with n8n-button at …
alexgrozav Jun 16, 2022
5635998
test: Updated button snapshot.
alexgrozav Jun 16, 2022
3ab8d12
fix: Fixed element-ui locally referenced buttons (via components: {}).
alexgrozav Jun 16, 2022
4fe07cd
fix: Updated colors. Removed irrelevant validation. Added ElButton ov…
alexgrozav Jun 20, 2022
4fc7506
test: Updated button override snapshot.
alexgrozav Jun 20, 2022
77d970e
fix: Various button adjustments and fixes.
alexgrozav Jun 20, 2022
742888b
chore: Merged master into n8n-3750-redesign-button-component-2.
alexgrozav Jun 20, 2022
f079bf6
chore: Merge master into n8n-3750-redesign-button-component-2.
alexgrozav Jun 22, 2022
2804fb6
fix: Updated button disabled state.
alexgrozav Jun 28, 2022
edd04d5
test: Updated snapshots.
alexgrozav Jun 28, 2022
582f61b
Merge branch 'master' into n8n-3750-redesign-button-component-2
alexgrozav Jun 29, 2022
106397b
fix: Consolidated css variables changes.
alexgrozav Jul 6, 2022
916ec9a
Merge branch 'master' into n8n-3750-redesign-button-component-2
alexgrozav Jul 6, 2022
f6cb28f
chore: Merged branch master into n8n-3750-redesign-button-component-2.
alexgrozav Jul 18, 2022
d223080
Data pinning (#3512)
alexgrozav Jul 18, 2022
ed3dceb
fix: Showing pin data without executing the node only in output pane.
alexgrozav Jul 18, 2022
7d8e39f
fix: Updated no data message when previous node not executed.
alexgrozav Jul 19, 2022
84c598a
feat: Added expression input and evaluation for pin data nodes withou…
alexgrozav Jul 20, 2022
8d1ee92
chore: Fixed linting issues and removed remnant console.log().
alexgrozav Jul 20, 2022
eb7fa9b
chore: Undone package-lock changes.
alexgrozav Jul 20, 2022
fdf2392
fix: Removed pin data store changes.
alexgrozav Jul 20, 2022
e51d126
fix: Created a new object using vuex runExecutionData.
alexgrozav Jul 20, 2022
0f92213
fix: Fixed bug appearing when adding a new node after executing.
alexgrozav Jul 20, 2022
95365f3
fix: Fix editor-ui build
ivov Jul 20, 2022
cf0568c
feat: Added green node connectors when having pin data output.
alexgrozav Jul 20, 2022
7feed12
Merge branch 'n8n-4143-make-node-parameter-variables-and' of github.c…
alexgrozav Jul 20, 2022
3cd16c3
chore: Fixed linting errors.
alexgrozav Jul 20, 2022
b18309f
fix: Added pin data eventBus unsubscribe.
alexgrozav Jul 20, 2022
030c9fa
fix: Added pin data color check after adding a connection.
alexgrozav Jul 20, 2022
e1c12ba
Merge pull request #3738 from n8n-io/n8n-4143-make-node-parameter-var…
alexgrozav Jul 20, 2022
4ccab29
:twisted_rightwards_arrows: Merge master
ivov Jul 20, 2022
9f4b37f
:art: Add pindata styles
ivov Jul 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/cli/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ITelemetrySettings,
ITelemetryTrackProperties,
IWorkflowBase as IWorkflowBaseWorkflow,
PinData,
Workflow,
WorkflowExecuteMode,
} from 'n8n-workflow';
Expand Down Expand Up @@ -688,6 +689,7 @@ export interface IWorkflowExecutionDataProcess {
executionMode: WorkflowExecuteMode;
executionData?: IRunExecutionData;
runData?: IRunData;
pinData?: PinData;
retryOf?: number | string;
sessionId?: string;
startNodes?: string[];
Expand Down
154 changes: 37 additions & 117 deletions packages/cli/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import {
IWorkflowBase,
LoggerProxy,
NodeHelpers,
PinData,
WebhookHttpMethod,
Workflow,
WorkflowExecuteMode,
Expand Down Expand Up @@ -130,6 +131,7 @@ import {
WorkflowRunner,
getCredentialForUser,
getCredentialWithoutUser,
IWorkflowDb,
} from '.';

import config from '../config';
Expand Down Expand Up @@ -157,9 +159,9 @@ import type {
} from './requests';
import { DEFAULT_EXECUTIONS_GET_ALL_LIMIT, validateEntity } from './GenericHelpers';
import { ExecutionEntity } from './databases/entities/ExecutionEntity';
import { SharedWorkflow } from './databases/entities/SharedWorkflow';
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from './constants';
import { credentialsController } from './api/credentials.api';
import { workflowsController } from './api/workflows.api';
import { nodesController } from './api/nodes.api';
import { oauth2CredentialController } from './api/oauth2Credential.api';
import {
Expand All @@ -168,6 +170,7 @@ import {
isUserManagementEnabled,
} from './UserManagement/UserManagementHelper';
import { loadPublicApiVersions } from './PublicApi';
import { SharedWorkflow } from './databases/entities/SharedWorkflow';

require('body-parser-xml')(bodyParser);

Expand Down Expand Up @@ -769,74 +772,6 @@ class App {
// Workflow
// ----------------------------------------

// Creates a new workflow
this.app.post(
`/${this.restEndpoint}/workflows`,
ResponseHelper.send(async (req: WorkflowRequest.Create) => {
delete req.body.id; // delete if sent

const newWorkflow = new WorkflowEntity();

Object.assign(newWorkflow, req.body);

await validateEntity(newWorkflow);

await this.externalHooks.run('workflow.create', [newWorkflow]);

const { tags: tagIds } = req.body;

if (tagIds?.length && !config.getEnv('workflowTagsDisabled')) {
newWorkflow.tags = await Db.collections.Tag.findByIds(tagIds, {
select: ['id', 'name'],
});
}

await WorkflowHelpers.replaceInvalidCredentials(newWorkflow);

let savedWorkflow: undefined | WorkflowEntity;

await getConnection().transaction(async (transactionManager) => {
savedWorkflow = await transactionManager.save<WorkflowEntity>(newWorkflow);

const role = await Db.collections.Role.findOneOrFail({
name: 'owner',
scope: 'workflow',
});

const newSharedWorkflow = new SharedWorkflow();

Object.assign(newSharedWorkflow, {
role,
user: req.user,
workflow: savedWorkflow,
});

await transactionManager.save<SharedWorkflow>(newSharedWorkflow);
});

if (!savedWorkflow) {
LoggerProxy.error('Failed to create workflow', { userId: req.user.id });
throw new ResponseHelper.ResponseError('Failed to save workflow');
}

if (tagIds && !config.getEnv('workflowTagsDisabled')) {
savedWorkflow.tags = TagHelpers.sortByRequestOrder(savedWorkflow.tags, {
requestOrder: tagIds,
});
}

await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]);
void InternalHooksManager.getInstance().onWorkflowCreated(req.user.id, newWorkflow, false);

const { id, ...rest } = savedWorkflow;

return {
id: id.toString(),
...rest,
};
}),
);

// Reads and returns workflow data from an URL
this.app.get(
`/${this.restEndpoint}/workflows/from-url`,
Expand Down Expand Up @@ -962,50 +897,6 @@ class App {
}),
);

// Returns a specific workflow
this.app.get(
`/${this.restEndpoint}/workflows/:id`,
ResponseHelper.send(async (req: WorkflowRequest.Get) => {
const { id: workflowId } = req.params;

let relations = ['workflow', 'workflow.tags'];

if (config.getEnv('workflowTagsDisabled')) {
relations = relations.filter((relation) => relation !== 'workflow.tags');
}

const shared = await Db.collections.SharedWorkflow.findOne({
relations,
where: whereClause({
user: req.user,
entityType: 'workflow',
entityId: workflowId,
}),
});

if (!shared) {
LoggerProxy.info('User attempted to access a workflow without permissions', {
workflowId,
userId: req.user.id,
});
throw new ResponseHelper.ResponseError(
`Workflow with ID "${workflowId}" could not be found.`,
undefined,
404,
);
}

const {
workflow: { id, ...rest },
} = shared;

return {
id: id.toString(),
...rest,
};
}),
);

// Updates an existing workflow
this.app.patch(
`/${this.restEndpoint}/workflows/:id`,
Expand Down Expand Up @@ -1204,19 +1095,23 @@ class App {
): Promise<IExecutionPushResponse> => {
const { workflowData } = req.body;
const { runData } = req.body;
const { pinData } = req.body;
const { startNodes } = req.body;
const { destinationNode } = req.body;
const executionMode = 'manual';
const activationMode = 'manual';

const sessionId = GenericHelpers.getSessionId(req);

const pinnedTrigger = findFirstPinnedTrigger(workflowData, pinData);

// If webhooks nodes exist and are active we have to wait for till we receive a call
if (
runData === undefined ||
startNodes === undefined ||
startNodes.length === 0 ||
destinationNode === undefined
pinnedTrigger === undefined &&
(runData === undefined ||
startNodes === undefined ||
startNodes.length === 0 ||
destinationNode === undefined)
) {
const additionalData = await WorkflowExecuteAdditionalData.getBase(req.user.id);
const nodeTypes = NodeTypes();
Expand Down Expand Up @@ -1254,11 +1149,17 @@ class App {
destinationNode,
executionMode,
runData,
pinData,
sessionId,
startNodes,
workflowData,
userId: req.user.id,
};

if (pinnedTrigger) {
data.startNodes = [pinnedTrigger.name];
}

const workflowRunner = new WorkflowRunner();
const executionId = await workflowRunner.run(data);

Expand All @@ -1269,6 +1170,8 @@ class App {
),
);

this.app.use(`/${this.restEndpoint}/workflows`, workflowsController);

// Retrieves all tags, with or without usage count
this.app.get(
`/${this.restEndpoint}/tags`,
Expand Down Expand Up @@ -2927,3 +2830,20 @@ function isOAuth(credType: ICredentialType) {
)
);
}

const TRIGGER_NODE_SUFFIXES = ['trigger', 'webhook'];

const isTrigger = (str: string) =>
TRIGGER_NODE_SUFFIXES.some((suffix) => str.toLowerCase().includes(suffix));

function findFirstPinnedTrigger(workflow: IWorkflowDb, pinData?: PinData) {
if (!pinData) return;

const firstPinnedTriggerName = Object.keys(pinData).find(isTrigger);

if (!firstPinnedTriggerName) return;

return workflow.nodes.find(
({ type, name }) => isTrigger(type) && name === firstPinnedTriggerName,
);
}
4 changes: 4 additions & 0 deletions packages/cli/src/WebhookHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@ export async function executeWebhook(
return undefined;
}

if (workflowData.pinData) {
data.data.resultData.pinData = workflowData.pinData;
}

const returnData = WorkflowHelpers.getDataLastExecutedNodeData(data);
if (data.data.resultData.error || returnData?.error !== undefined) {
if (!didSendResponse) {
Expand Down
23 changes: 21 additions & 2 deletions packages/cli/src/WorkflowHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const ERROR_TRIGGER_TYPE = config.getEnv('nodes.errorTriggerType');
* @returns {(ITaskData | undefined)}
*/
export function getDataLastExecutedNodeData(inputData: IRun): ITaskData | undefined {
const { runData } = inputData.data.resultData;
const { runData, pinData = {} } = inputData.data.resultData;
const { lastNodeExecuted } = inputData.data.resultData;

if (lastNodeExecuted === undefined) {
Expand All @@ -61,7 +61,26 @@ export function getDataLastExecutedNodeData(inputData: IRun): ITaskData | undefi
return undefined;
}

return runData[lastNodeExecuted][runData[lastNodeExecuted].length - 1];
const lastNodeRunData = runData[lastNodeExecuted][runData[lastNodeExecuted].length - 1];

let lastNodePinData = pinData[lastNodeExecuted];

if (lastNodePinData) {
if (!Array.isArray(lastNodePinData)) lastNodePinData = [lastNodePinData];

const itemsPerRun = lastNodePinData.map((item, index) => {
return { json: item, pairedItem: { item: index } };
});

return {
startTime: 0,
executionTime: 0,
data: { main: [itemsPerRun] },
source: lastNodeRunData.source,
};
}

return lastNodeRunData;
}

/**
Expand Down
8 changes: 7 additions & 1 deletion packages/cli/src/WorkflowRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,12 @@ export class WorkflowRunner {

// Can execute without webhook so go on
const workflowExecute = new WorkflowExecute(additionalData, data.executionMode);
workflowExecution = workflowExecute.run(workflow, undefined, data.destinationNode);
workflowExecution = workflowExecute.run(
workflow,
undefined,
data.destinationNode,
data.pinData,
);
} else {
Logger.debug(`Execution ID ${executionId} is a partial execution.`, { executionId });
// Execute only the nodes between start and destination nodes
Expand All @@ -320,6 +325,7 @@ export class WorkflowRunner {
data.runData,
data.startNodes,
data.destinationNode,
data.pinData,
);
}

Expand Down
16 changes: 15 additions & 1 deletion packages/cli/src/WorkflowRunnerProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,22 @@ export class WorkflowRunnerProcess {
) {
// Execute all nodes

let startNode;
if (
this.data.startNodes?.length === 1 &&
Object.keys(this.data.pinData ?? {}).includes(this.data.startNodes[0])
) {
startNode = this.workflow.getNode(this.data.startNodes[0]) ?? undefined;
}

// Can execute without webhook so go on
this.workflowExecute = new WorkflowExecute(additionalData, this.data.executionMode);
return this.workflowExecute.run(this.workflow, undefined, this.data.destinationNode);
return this.workflowExecute.run(
this.workflow,
startNode,
this.data.destinationNode,
this.data.pinData,
);
}
// Execute only the nodes between start and destination nodes
this.workflowExecute = new WorkflowExecute(additionalData, this.data.executionMode);
Expand All @@ -356,6 +369,7 @@ export class WorkflowRunnerProcess {
this.data.runData,
this.data.startNodes,
this.data.destinationNode,
this.data.pinData,
);
}

Expand Down
Loading