n8n/packages/cli/src/api/workflowStats.api.ts
Omar Ajoue 25e9f0817a
refactor: Workflow sharing bug bash fixes (#4888)
* fix: Prevent workflows with only manual trigger from being activated

* fix: Fix workflow id when sharing from workflows list

* fix: Update sharing modal translations

* fix: Allow sharees to disable workflows and fix issue with unique key when removing a user

* refactor: Improve error messages and change logging level to be less verbose

* fix: Broken user removal transfer issue

* feat: Implement workflow sharing BE telemetry

* chore: temporarily add sharing env vars

* feat: Implement BE telemetry for workflow sharing

* fix: Prevent issues with possibly missing workflow id

* feat: Replace WorkflowSharing flag references (no-changelog) (#4918)

* ci: Block all external network calls in tests (no-changelog) (#4930)

* setup nock to prevent tests from making any external requests

* mock all calls to posthog sdk

* feat: Replace WorkflowSharing flag references (no-changelog)

Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <netroy@users.noreply.github.com>

* refactor: Remove temporary feature flag for workflow sharing

* refactor: add sharing_role to both manual and node executions

* refactor: Allow changing name, position and disabled of read only nodes

* feat: Overhaul dynamic translations for local and cloud (#4943)

* feat: Overhaul dynamic translations for local and cloud

* fix: remove type casting

* chore: remove unused translations

* fix: fix workflow sharing translation

* test: Fix broken test

* refactor: remove unnecessary import

* refactor: Minor code improvements

* refactor: rename dynamicTranslations to contextBasedTranslationKeys

* fix: fix type imports

* refactor: Consolidate sharing feature check

* feat: update cred sharing unavailable translations

* feat: update upgrade message when user management not available

* fix: rename plan names to Pro and Power

* feat: update translations to no longer contain plan names

* wip: subworkflow permissions

* feat: add workflowsFromSameOwner caller policy

* feat: Fix subworkflow permissions

* shared entites should check for role when deleting users

* refactor: remove circular dependency

* role filter shouldn't be an array

* fixed role issue

* fix: Corrected behavior when removing users

* feat: show instance owner credential sharing message only if isnt sharee

* feat: update workflow caller policy caller ids labels

* feat: update upgrade plan links to contain instance ids

* fix: show check errors below creds message only to owner

* fix(editor): Hide usage page on cloud

* fix: update credential validation error message for sharee

* fix(core): Remove duplicate import

* fix(editor): Extending deployment types

* feat: Overhaul contextual translations (#4992)

feat: update how contextual translations work

* refactor: improve messageing for subworkflow permissions

* test: Fix issue with user deletion and transfer

* fix: Explicitly throw error message so it can be displayed in UI

Co-authored-by: Alex Grozav <alex@grozav.com>
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <netroy@users.noreply.github.com>
Co-authored-by: freyamade <freya@n8n.io>
Co-authored-by: Csaba Tuncsik <csaba@n8n.io>
2022-12-21 16:42:07 +01:00

186 lines
4.4 KiB
TypeScript

import { User } from '@/databases/entities/User';
import { whereClause } from '@/UserManagement/UserManagementHelper';
import express from 'express';
import { LoggerProxy } from 'n8n-workflow';
import * as Db from '@/Db';
import * as ResponseHelper from '@/ResponseHelper';
import type {
IWorkflowStatisticsCounts,
IWorkflowStatisticsDataLoaded,
IWorkflowStatisticsTimestamps,
} from '@/Interfaces';
import { StatisticsNames } from '../databases/entities/WorkflowStatistics';
import { getLogger } from '../Logger';
import { ExecutionRequest } from '../requests';
export const workflowStatsController = express.Router();
// Helper function that validates the ID, return a flag stating whether the request is allowed
async function checkWorkflowId(workflowId: string, user: User): Promise<boolean> {
// Check permissions
const shared = await Db.collections.SharedWorkflow.findOne({
relations: ['workflow'],
where: whereClause({
user,
entityType: 'workflow',
entityId: workflowId,
}),
});
if (!shared) {
LoggerProxy.verbose('User attempted to read a workflow without permissions', {
workflowId,
userId: user.id,
});
return false;
}
return true;
}
/**
* Initialise Logger if needed
*/
workflowStatsController.use((req, res, next) => {
try {
LoggerProxy.getInstance();
} catch (error) {
LoggerProxy.init(getLogger());
}
next();
});
/**
* Check that the workflow ID is valid and allowed to be read by the user
*/
workflowStatsController.use(async (req: ExecutionRequest.Get, res, next) => {
const allowed = await checkWorkflowId(req.params.id, req.user);
if (allowed) {
next();
} else {
// Otherwise, make and return an error
const response = new ResponseHelper.NotFoundError(`Workflow ${req.params.id} does not exist.`);
next(response);
}
});
/**
* GET /workflow-stats/:id/counts/
*/
workflowStatsController.get(
'/:id/counts/',
ResponseHelper.send(async (req: ExecutionRequest.Get): Promise<IWorkflowStatisticsCounts> => {
// Get counts from DB
const workflowId = req.params.id;
// Find the stats for this workflow
const stats = await Db.collections.WorkflowStatistics.find({
select: ['count', 'name'],
where: {
workflowId,
},
});
const data: IWorkflowStatisticsCounts = {
productionSuccess: 0,
productionError: 0,
manualSuccess: 0,
manualError: 0,
};
// There will be a maximum of 4 stats (currently)
stats.forEach(({ count, name }) => {
switch (name) {
case StatisticsNames.manualError:
data.manualError = count;
break;
case StatisticsNames.manualSuccess:
data.manualSuccess = count;
break;
case StatisticsNames.productionError:
data.productionError = count;
break;
case StatisticsNames.productionSuccess:
data.productionSuccess = count;
}
});
return data;
}),
);
/**
* GET /workflow-stats/:id/times/
*/
workflowStatsController.get(
'/:id/times/',
ResponseHelper.send(async (req: ExecutionRequest.Get): Promise<IWorkflowStatisticsTimestamps> => {
// Get times from DB
const workflowId = req.params.id;
// Find the stats for this workflow
const stats = await Db.collections.WorkflowStatistics.find({
select: ['latestEvent', 'name'],
where: {
workflowId,
},
});
const data: IWorkflowStatisticsTimestamps = {
productionSuccess: null,
productionError: null,
manualSuccess: null,
manualError: null,
};
// There will be a maximum of 4 stats (currently)
stats.forEach(({ latestEvent, name }) => {
switch (name) {
case StatisticsNames.manualError:
data.manualError = latestEvent;
break;
case StatisticsNames.manualSuccess:
data.manualSuccess = latestEvent;
break;
case StatisticsNames.productionError:
data.productionError = latestEvent;
break;
case StatisticsNames.productionSuccess:
data.productionSuccess = latestEvent;
}
});
return data;
}),
);
/**
* GET /workflow-stats/:id/data-loaded/
*/
workflowStatsController.get(
'/:id/data-loaded/',
ResponseHelper.send(async (req: ExecutionRequest.Get): Promise<IWorkflowStatisticsDataLoaded> => {
// Get flag
const workflowId = req.params.id;
// Get the corresponding workflow
const workflow = await Db.collections.Workflow.findOne(workflowId);
// It will be valid if we reach this point, this is just for TS
if (!workflow) {
return { dataLoaded: false };
}
const data: IWorkflowStatisticsDataLoaded = {
dataLoaded: workflow.dataLoaded,
};
return data;
}),
);