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

feat(editor): In-app experience #4875

Merged
merged 94 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
fc6f978
feat: Add license quotas endpoint
krynble Dec 1, 2022
efbfc8c
feat: Add trigger count to workflow activation process
krynble Dec 5, 2022
ce2693d
merge master
krynble Dec 6, 2022
7ab78f2
refactor: Get quotas from db
krynble Dec 6, 2022
edd9bf4
Merge branch 'master' into pay-4-quotas
krynble Dec 6, 2022
163fcba
feat: Add license information
krynble Dec 7, 2022
8a90395
:sparkles: - finalised GET /license endpoint
Dec 7, 2022
a0ec6d6
:hammer: - getActiveTriggerCount return 0 instead of null
Dec 7, 2022
e904ae5
:bug: - ignore manualTrigger when counting active triggers
Dec 7, 2022
7b0bc25
:sparkles: - add activation endpoint
Dec 8, 2022
53522e5
:sparkles: - added renew endpoint
Dec 8, 2022
cb0e190
:hammer: - added return type interfaces
Dec 8, 2022
7afd7e1
:hammer: - handle license errors where methods are called
Dec 8, 2022
c94ddaa
:hammer: - rename function to match name from lib
Dec 8, 2022
bfe0eb5
feat(editor): usage add plans buttons logic
cstuncsik Dec 9, 2022
c262566
:rotating_light: - testing new License methods
Dec 9, 2022
f2123da
feat(editor): usage add more business logic
cstuncsik Dec 9, 2022
9fb88bc
chore(editor): code formatting
cstuncsik Dec 9, 2022
ea6029b
:rotating_light: - added license api tests
Dec 9, 2022
0e91cfe
fix(editor): usage store
cstuncsik Dec 9, 2022
31ae644
Merge branch 'feature/usage-and-plan' of github.com:n8n-io/n8n into n…
cstuncsik Dec 9, 2022
89cd124
fix(editor): usage update translations
cstuncsik Dec 9, 2022
7aae4ad
feat(editor): usage add license activation modal
cstuncsik Dec 9, 2022
6d7ac9c
feat(editor): usage change subscription app url
cstuncsik Dec 9, 2022
bd0258d
feat(editor): usage add contact us link
cstuncsik Dec 9, 2022
ad8bdb8
feat(editor): usage fix modal width
cstuncsik Dec 9, 2022
97e380f
Merge branch 'master' of github.com:n8n-io/n8n into pay-4-quotas
Dec 9, 2022
f92e6f8
:sparkles: - Add renewal tracking metric
Dec 9, 2022
842fc2a
Merge branch 'feature/usage-and-plan' of github.com:n8n-io/n8n into n…
cstuncsik Dec 12, 2022
fc53324
Merge branch 'pay-4-quotas' of github.com:n8n-io/n8n into n8n-5859-in…
cstuncsik Dec 12, 2022
2da3c60
:sparkles: - add license data to pulse event
Dec 12, 2022
84824fd
:hammer: - set default triggercount on entity model
Dec 12, 2022
1a3c184
:sparkles: - add db migrations for mysql and postgres
Dec 12, 2022
64a2f4f
fix(editor): Usage api call data processing and error handling
cstuncsik Dec 12, 2022
6fddab3
fix(editor): Usage fix activation query key
cstuncsik Dec 12, 2022
05cd1e4
:rotating_light: - add initDb to telemetry tests
Dec 12, 2022
8190f82
:hammer: - move getlicensedata to licenseservice
Dec 12, 2022
9266795
:hammer: - return 403 instead of 404 to non owners
Dec 12, 2022
d4d4864
:hammer: - move owner checking to middleware
Dec 12, 2022
1da82ee
:bug: - fixed incorrectly returned error from middleware
Dec 12, 2022
e12669a
:bug: - using mock instead of test db for pulse tests
Dec 12, 2022
df8a542
fix(editor): Usage fix activation and add success messages
cstuncsik Dec 12, 2022
a8b49b8
fix(editor): Usage should not renew activation right after activation
cstuncsik Dec 12, 2022
3d5c131
Merge remote-tracking branch 'origin/master' into feature/usage-and-plan
cstuncsik Dec 13, 2022
d9fe732
:rotating_light: - skipping failing pulse tests for now
Dec 13, 2022
c63a41c
Merge branch 'pay-4-quotas' of github.com:n8n-io/n8n into n8n-5859-in…
cstuncsik Dec 13, 2022
f473f5c
fix(editor): Usage add telemetry calls and apply design review outcomes
cstuncsik Dec 13, 2022
5c9c0f0
Merge branch 'feature/usage-and-plan' of github.com:n8n-io/n8n into n…
cstuncsik Dec 14, 2022
342fdeb
Merge branch 'feature/usage-and-plan' of github.com:n8n-io/n8n into n…
cstuncsik Dec 14, 2022
1ec65c8
feat(editor): Hide usage page according to BE flag
cstuncsik Dec 14, 2022
9fd60d8
feat(editor): Usage modify key activation flow
cstuncsik Dec 14, 2022
7ba74ad
feat(editor): Usage change subscription app url
cstuncsik Dec 14, 2022
03e68c9
feat(editor): Usage add telemetry for manage plan
cstuncsik Dec 14, 2022
d94c3b3
feat(editor): Usage extend link url query params
cstuncsik Dec 14, 2022
a8b4808
feat(editor): Usage add line chart if there is a workflow limit
cstuncsik Dec 14, 2022
36c3742
feat(editor): Usage remove query after key activation redirection
cstuncsik Dec 14, 2022
ffe6fd1
fix(editor): Usage handle limit exceeded workflow chart, add focus to…
cstuncsik Dec 14, 2022
6e662c9
fix(editor): Usage activation can return router promise when removing…
cstuncsik Dec 14, 2022
f40fdff
fix(editor): Usage and plan design review
cstuncsik Dec 15, 2022
01bda61
:bug: - fix renew endpoint hanging issue
Dec 15, 2022
04c7e01
:bug: - fix license activation bug
Dec 15, 2022
f85f563
fix(editor): Usage proper translation for plans and/or editions
cstuncsik Dec 15, 2022
d7535fc
Merge branch 'n8n-5859-in-app-plans-experience' of github.com:n8n-io/…
cstuncsik Dec 15, 2022
b7a2d7c
fix(editor): Usage apply David's review results
cstuncsik Dec 15, 2022
e9f318d
fix(editor): Usage page set as default and first under Settings
cstuncsik Dec 15, 2022
f3d6d3f
fix(editor): Usage open subscription app in new tab
cstuncsik Dec 16, 2022
36a1354
fix(editor): Usage page having key query param a plan links
cstuncsik Dec 16, 2022
26d2bbf
test: Fix broken test
krynble Dec 16, 2022
f13dcdd
fix(editor): Usage page address review
cstuncsik Dec 16, 2022
92c5f3e
:test_tube: Flush promises on telemetry tests
ivov Dec 14, 2022
91e86e7
:zap: Extract helper with `setImmediate`
ivov Dec 14, 2022
fa56008
:fire: Remove leftovers
ivov Dec 14, 2022
e8d90cf
:zap: Use Adi's helper
ivov Dec 15, 2022
4027d70
Merge branch 'n8n-5859-in-app-plans-experience' of github.com:n8n-io/…
cstuncsik Dec 16, 2022
b350813
refactor: Comment broken tests
krynble Dec 16, 2022
31442de
Merge branch 'n8n-5859-in-app-plans-experience' of github.com:n8n-io/…
krynble Dec 16, 2022
de64b48
refactor: add Tenant id to settings
krynble Dec 16, 2022
57962e2
feat: add environment to license endpoints
krynble Dec 16, 2022
47c5a90
refactor: Move license environment to general settings
krynble Dec 16, 2022
6cd9dbe
fix: fix routing bug
mutdmour Dec 16, 2022
605fd4c
Merge branch 'feature/usage-and-plan' into n8n-5859-in-app-plans-expe…
cstuncsik Dec 18, 2022
218a5c2
fix(editor): Usage page some code review changes and formatting
cstuncsik Dec 19, 2022
46be2a3
fix(editor): Usage page remove direct usage of reusable translation keys
cstuncsik Dec 19, 2022
d28dceb
fix(editor): Usage page async await instead of then
cstuncsik Dec 19, 2022
5f7beb7
fix(editor): Usage page show some content only if network requests in…
cstuncsik Dec 19, 2022
8b74654
chore(editor): code formatting
cstuncsik Dec 19, 2022
b7b18a7
fix(editor): Usage checking license environment
cstuncsik Dec 19, 2022
2a61693
feat(editor): Improve license activation error messages (no-changelog…
csuermann Dec 19, 2022
b937916
fix(editor): Usage changing activation error title
cstuncsik Dec 19, 2022
20cbd44
remove unnecessary import
krynble Dec 19, 2022
1a1429f
Merge branch 'n8n-5859-in-app-plans-experience' of github.com:n8n-io/…
krynble Dec 19, 2022
4c3b740
fix(editor): Usage refactor notification showing
cstuncsik Dec 19, 2022
fb1db18
Merge branch 'n8n-5859-in-app-plans-experience' of github.com:n8n-io/…
cstuncsik Dec 19, 2022
03ddbda
fix(editor): Usage using notification directly in store actions
cstuncsik Dec 19, 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: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"tsconfig-paths": "^3.14.1"
},
"dependencies": {
"@n8n_io/license-sdk": "^1.6.1",
"@n8n_io/license-sdk": "^1.7.0",
"@oclif/command": "^1.8.16",
"@oclif/core": "^1.16.4",
"@oclif/errors": "^1.3.6",
Expand Down
14 changes: 14 additions & 0 deletions packages/cli/src/ActiveWorkflowRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
WorkflowExecuteMode,
LoggerProxy as Logger,
ErrorReporterProxy as ErrorReporter,
INodeType,
} from 'n8n-workflow';

import express from 'express';
Expand Down Expand Up @@ -800,6 +801,7 @@ export class ActiveWorkflowRunner {

const canBeActivated = workflowInstance.checkIfWorkflowCanBeActivated([
'n8n-nodes-base.start',
'n8n-nodes-base.manualTrigger',
]);
if (!canBeActivated) {
Logger.error(`Unable to activate workflow "${workflowData.name}"`);
Expand Down Expand Up @@ -854,6 +856,18 @@ export class ActiveWorkflowRunner {
// If there were activation errors delete them
delete this.activationErrors[workflowId];
}

if (workflowInstance.id) {
// Sum all triggers in the workflow, EXCLUDING the manual trigger
const triggerFilter = (nodeType: INodeType) =>
!!nodeType.trigger && !nodeType.description.name.includes('manualTrigger');
const triggerCount =
workflowInstance.queryNodes(triggerFilter).length +
workflowInstance.getPollNodes().length +
WebhookHelpers.getWorkflowWebhooks(workflowInstance, additionalData, undefined, true)
.length;
await Db.collections.Workflow.update(workflowInstance.id, { triggerCount });
}
} catch (error) {
// There was a problem activating the workflow

Expand Down
25 changes: 25 additions & 0 deletions packages/cli/src/Interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,9 @@ export interface IN8nUISettings {
workflowSharing: boolean;
};
hideUsagePage: boolean;
license: {
environment: 'production' | 'staging';
};
}

export interface IPersonalizationSurveyAnswers {
Expand Down Expand Up @@ -751,3 +754,25 @@ export interface IExecutionTrackProperties extends ITelemetryTrackProperties {
error_node_type?: string;
is_manual: boolean;
}

// ----------------------------------
// license
// ----------------------------------

export interface ILicenseReadResponse {
usage: {
executions: {
limit: number;
value: number;
warningThreshold: number;
};
};
license: {
planId: string;
planName: string;
};
}

export interface ILicensePostResponse extends ILicenseReadResponse {
managementToken: string;
}
7 changes: 7 additions & 0 deletions packages/cli/src/InternalHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,4 +498,11 @@ export class InternalHooksClass implements IInternalHooksClass {
}): Promise<void> {
return this.telemetry.track('Workflow first data fetched', data, { withPostHog: true });
}

/**
* License
*/
async onLicenseRenewAttempt(data: { success: boolean }): Promise<void> {
await this.telemetry.track('Instance attempted to refresh license', data);
}
}
72 changes: 53 additions & 19 deletions packages/cli/src/License.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LicenseManager, TLicenseContainerStr } from '@n8n_io/license-sdk';
import { LicenseManager, TEntitlement, TLicenseContainerStr } from '@n8n_io/license-sdk';
import { ILogger } from 'n8n-workflow';
import { getLogger } from './Logger';
import config from '@/config';
Expand Down Expand Up @@ -70,31 +70,15 @@ export class License {
return;
}

if (this.manager.isValid()) {
return;
}

try {
await this.manager.activate(activationKey);
} catch (e) {
if (e instanceof Error) {
this.logger.error('Could not activate license', e);
}
}
await this.manager.activate(activationKey);
}

async renew() {
if (!this.manager) {
return;
}

try {
await this.manager.renew();
} catch (e) {
if (e instanceof Error) {
this.logger.error('Could not renew license', e);
}
}
await this.manager.renew();
}

isFeatureEnabled(feature: string): boolean {
Expand All @@ -108,6 +92,56 @@ export class License {
isSharingEnabled() {
return this.isFeatureEnabled(LICENSE_FEATURES.SHARING);
}

getCurrentEntitlements() {
return this.manager?.getCurrentEntitlements() ?? [];
}

getFeatureValue(
feature: string,
requireValidCert?: boolean,
): undefined | boolean | number | string {
if (!this.manager) {
return undefined;
}

return this.manager.getFeatureValue(feature, requireValidCert);
}

getManagementJwt(): string {
if (!this.manager) {
return '';
}
return this.manager.getManagementJwt();
}

/**
* Helper function to get the main plan for a license
*/
getMainPlan(): TEntitlement | undefined {
if (!this.manager) {
return undefined;
}

const entitlements = this.getCurrentEntitlements();
if (!entitlements.length) {
return undefined;
}

return entitlements.find(
(entitlement) =>
(entitlement.productMetadata.terms as unknown as { isMainPlan: boolean }).isMainPlan,
);
}

// Helper functions for computed data
getTriggerLimit(): number {
return (this.getFeatureValue('quota:activeWorkflows') ?? -1) as number;
}

getPlanName(): string {
return (this.getFeatureValue('planName') ?? 'Community') as string;
}
}

let licenseInstance: License | undefined;
Expand Down
15 changes: 14 additions & 1 deletion packages/cli/src/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'
import { toHttpNodeParameters } from '@/CurlConverterHelper';
import { setupErrorMiddleware } from '@/ErrorReporting';
import { getLicense } from '@/License';
import { licenseController } from './license/license.controller';
import { corsMiddleware } from './middlewares/cors';

require('body-parser-xml')(bodyParser);
Expand Down Expand Up @@ -358,6 +359,9 @@ class App {
workflowSharing: false,
},
hideUsagePage: config.getEnv('hideUsagePage'),
license: {
environment: config.getEnv('license.tenantId') === 1 ? 'production' : 'staging',
},
};
}

Expand Down Expand Up @@ -401,7 +405,11 @@ class App {

const activationKey = config.getEnv('license.activationKey');
if (activationKey) {
await license.activate(activationKey);
try {
await license.activate(activationKey);
} catch (e) {
LoggerProxy.error('Could not activate license', e);
}
}
}

Expand Down Expand Up @@ -792,6 +800,11 @@ class App {
// ----------------------------------------
this.app.use(`/${this.restEndpoint}/workflows`, workflowsController);

// ----------------------------------------
// License
// ----------------------------------------
this.app.use(`/${this.restEndpoint}/license`, licenseController);

// ----------------------------------------
// Workflow Statistics
// ----------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/WebhookHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ import { getWorkflowOwner } from '@/UserManagement/UserManagementHelper';
export const WEBHOOK_METHODS = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'];

/**
* Returns all the webhooks which should be created for the give workflow
* Returns all the webhooks which should be created for the given workflow
*
*/
export function getWorkflowWebhooks(
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/databases/entities/WorkflowEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ export class WorkflowEntity extends AbstractEntity implements IWorkflowDb {

@Column({ length: 36 })
versionId: string;

@Column({ default: 0 })
triggerCount: number;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import { logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers';
import config from '@/config';

export class AddTriggerCountColumn1669823906994 implements MigrationInterface {
name = 'AddTriggerCountColumn1669823906994';

async up(queryRunner: QueryRunner): Promise<void> {
logMigrationStart(this.name);

const tablePrefix = config.getEnv('database.tablePrefix');

await queryRunner.query(
`ALTER TABLE ${tablePrefix}workflow_entity ADD COLUMN triggerCount integer NOT NULL DEFAULT 0`,
);
// Table will be populated by n8n startup - see ActiveWorkflowRunner.ts

logMigrationEnd(this.name);
}

async down(queryRunner: QueryRunner): Promise<void> {
const tablePrefix = config.getEnv('database.tablePrefix');

await queryRunner.query(
`ALTER TABLE ${tablePrefix}workflow_entity DROP COLUMN triggerCount`,
);
}
}
2 changes: 2 additions & 0 deletions packages/cli/src/databases/migrations/mysqldb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { CreateWorkflowsEditorRole1663755770894 } from './1663755770894-CreateWo
import { CreateCredentialUsageTable1665484192213 } from './1665484192213-CreateCredentialUsageTable';
import { RemoveCredentialUsageTable1665754637026 } from './1665754637026-RemoveCredentialUsageTable';
import { AddWorkflowVersionIdColumn1669739707125 } from './1669739707125-AddWorkflowVersionIdColumn';
import { AddTriggerCountColumn1669823906994 } from './1669823906994-AddTriggerCountColumn';

export const mysqlMigrations = [
InitialMigration1588157391238,
Expand Down Expand Up @@ -54,4 +55,5 @@ export const mysqlMigrations = [
RemoveCredentialUsageTable1665754637026,
AddWorkflowVersionIdColumn1669739707125,
WorkflowStatistics1664196174002,
AddTriggerCountColumn1669823906994,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import { getTablePrefix, logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers';
import config from '@/config';

export class AddTriggerCountColumn1669823906995 implements MigrationInterface {
name = 'AddTriggerCountColumn1669823906995';

async up(queryRunner: QueryRunner): Promise<void> {
logMigrationStart(this.name);

const tablePrefix = getTablePrefix();

await queryRunner.query(
`ALTER TABLE ${tablePrefix}workflow_entity ADD COLUMN "triggerCount" integer NOT NULL DEFAULT 0`,
);
// Table will be populated by n8n startup - see ActiveWorkflowRunner.ts

logMigrationEnd(this.name);
}

async down(queryRunner: QueryRunner): Promise<void> {
const tablePrefix = getTablePrefix();

await queryRunner.query(
`ALTER TABLE ${tablePrefix}workflow_entity DROP COLUMN "triggerCount"`,
);
}
}
2 changes: 2 additions & 0 deletions packages/cli/src/databases/migrations/postgresdb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { CreateWorkflowsEditorRole1663755770893 } from './1663755770893-CreateWo
import { CreateCredentialUsageTable1665484192212 } from './1665484192212-CreateCredentialUsageTable';
import { RemoveCredentialUsageTable1665754637025 } from './1665754637025-RemoveCredentialUsageTable';
import { AddWorkflowVersionIdColumn1669739707126 } from './1669739707126-AddWorkflowVersionIdColumn';
import { AddTriggerCountColumn1669823906995 } from './1669823906995-AddTriggerCountColumn';

export const postgresMigrations = [
InitialMigration1587669153312,
Expand Down Expand Up @@ -50,4 +51,5 @@ export const postgresMigrations = [
RemoveCredentialUsageTable1665754637025,
AddWorkflowVersionIdColumn1669739707126,
WorkflowStatistics1664196174001,
AddTriggerCountColumn1669823906995,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import { logMigrationEnd, logMigrationStart } from '@db/utils/migrationHelpers';
import config from '@/config';

export class AddTriggerCountColumn1669823906993 implements MigrationInterface {
name = 'AddTriggerCountColumn1669823906993';

async up(queryRunner: QueryRunner): Promise<void> {
logMigrationStart(this.name);

const tablePrefix = config.getEnv('database.tablePrefix');

await queryRunner.query(
`ALTER TABLE \`${tablePrefix}workflow_entity\` ADD COLUMN "triggerCount" integer NOT NULL DEFAULT 0`,
);
// Table will be populated by n8n startup - see ActiveWorkflowRunner.ts

logMigrationEnd(this.name);
}

async down(queryRunner: QueryRunner): Promise<void> {
const tablePrefix = config.getEnv('database.tablePrefix');

await queryRunner.query(
`ALTER TABLE \`${tablePrefix}workflow_entity\` DROP COLUMN "triggerCount"`,
);
}
}
2 changes: 2 additions & 0 deletions packages/cli/src/databases/migrations/sqlite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { CreateWorkflowsEditorRole1663755770892 } from './1663755770892-CreateWo
import { CreateCredentialUsageTable1665484192211 } from './1665484192211-CreateCredentialUsageTable';
import { RemoveCredentialUsageTable1665754637024 } from './1665754637024-RemoveCredentialUsageTable';
import { AddWorkflowVersionIdColumn1669739707124 } from './1669739707124-AddWorkflowVersionIdColumn';
import { AddTriggerCountColumn1669823906993 } from './1669823906993-AddTriggerCountColumn';

const sqliteMigrations = [
InitialMigration1588102412422,
Expand All @@ -47,6 +48,7 @@ const sqliteMigrations = [
CreateCredentialUsageTable1665484192211,
RemoveCredentialUsageTable1665754637024,
AddWorkflowVersionIdColumn1669739707124,
AddTriggerCountColumn1669823906993,
WorkflowStatistics1664196174000,
];

Expand Down
Loading