From 9fc3699beb0c150909889ed17740a5cd9e0461c3 Mon Sep 17 00:00:00 2001 From: Eugene Date: Thu, 26 Sep 2024 12:03:29 +0200 Subject: [PATCH 001/178] feat(editor): Do not show error for remote options when credentials aren't specified (#10944) --- cypress/e2e/5-ndv.cy.ts | 22 ++++++++++ .../src/components/ParameterInput.vue | 10 +++++ .../__tests__/ParameterInput.test.ts | 43 +++++++++++++++++++ .../src/plugins/i18n/locales/en.json | 1 + 4 files changed, 76 insertions(+) diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 4608b5eefc..a591d62895 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -674,6 +674,23 @@ describe('NDV', () => { ndv.getters.parameterInput('operation').find('input').should('have.value', 'Delete'); }); + it('Should show a notice when remote options cannot be fetched because of missing credentials', () => { + cy.intercept('POST', '/rest/dynamic-node-parameters/options', { statusCode: 403 }).as( + 'parameterOptions', + ); + + workflowPage.actions.addInitialNodeToCanvas(NOTION_NODE_NAME, { + keepNdvOpen: true, + action: 'Update a database page', + }); + + ndv.actions.addItemToFixedCollection('propertiesUi'); + ndv.getters + .parameterInput('key') + .find('input') + .should('have.value', 'Set up credential to see options'); + }); + it('Should show error state when remote options cannot be fetched', () => { cy.intercept('POST', '/rest/dynamic-node-parameters/options', { statusCode: 500 }).as( 'parameterOptions', @@ -684,6 +701,11 @@ describe('NDV', () => { action: 'Update a database page', }); + clickCreateNewCredential(); + setCredentialValues({ + apiKey: 'sk_test_123', + }); + ndv.actions.addItemToFixedCollection('propertiesUi'); ndv.getters .parameterInput('key') diff --git a/packages/editor-ui/src/components/ParameterInput.vue b/packages/editor-ui/src/components/ParameterInput.vue index 80d0cd890b..313f62fe33 100644 --- a/packages/editor-ui/src/components/ParameterInput.vue +++ b/packages/editor-ui/src/components/ParameterInput.vue @@ -177,6 +177,15 @@ const displayValue = computed(() => { if (!nodeType.value || nodeType.value?.codex?.categories?.includes(CORE_NODES_CATEGORY)) { return i18n.baseText('parameterInput.loadOptionsError'); } + + if (nodeType.value?.credentials && nodeType.value?.credentials?.length > 0) { + const credentialsType = nodeType.value?.credentials[0]; + + if (credentialsType.required && !node.value?.credentials) { + return i18n.baseText('parameterInput.loadOptionsCredentialsRequired'); + } + } + return i18n.baseText('parameterInput.loadOptionsErrorService', { interpolate: { service: nodeType.value.displayName }, }); @@ -1275,6 +1284,7 @@ onUpdated(async () => { " :title="displayTitle" :placeholder="getPlaceholder()" + data-test-id="parameter-input-field" @update:model-value="(valueChanged($event) as undefined) && onUpdateTextInput($event)" @keydown.stop @focus="setFocus" diff --git a/packages/editor-ui/src/components/__tests__/ParameterInput.test.ts b/packages/editor-ui/src/components/__tests__/ParameterInput.test.ts index 76f8c95951..3deee4ef26 100644 --- a/packages/editor-ui/src/components/__tests__/ParameterInput.test.ts +++ b/packages/editor-ui/src/components/__tests__/ParameterInput.test.ts @@ -168,4 +168,47 @@ describe('ParameterInput.vue', () => { // Nothing should be emitted expect(emitted('update')).toBeUndefined(); }); + + test('should show message when can not load options without credentials', async () => { + mockNodeTypesState.getNodeParameterOptions = vi.fn(async () => { + throw new Error('Node does not have any credentials set'); + }); + + // @ts-expect-error Readonly property + mockNodeTypesState.getNodeType = vi.fn().mockReturnValue({ + displayName: 'Test', + credentials: [ + { + name: 'openAiApi', + required: true, + }, + ], + }); + + const { emitted, container, getByTestId } = renderComponent(ParameterInput, { + pinia: createTestingPinia(), + props: { + path: 'columns', + parameter: { + displayName: 'Columns', + name: 'columns', + type: 'options', + typeOptions: { loadOptionsMethod: 'getColumnsMultiOptions' }, + }, + modelValue: 'id', + }, + }); + + await waitFor(() => expect(getByTestId('parameter-input-field')).toBeInTheDocument()); + + const input = container.querySelector('input') as HTMLInputElement; + expect(input).toBeInTheDocument(); + + expect(mockNodeTypesState.getNodeParameterOptions).toHaveBeenCalled(); + + expect(input.value.toLowerCase()).not.toContain('error'); + expect(input).toHaveValue('Set up credential to see options'); + + expect(emitted('update')).toBeUndefined(); + }); }); diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index d007b41da1..6043c23d8b 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -1361,6 +1361,7 @@ "parameterInput.loadingOptions": "Loading options...", "parameterInput.loadOptionsErrorService": "Error fetching options from {service}", "parameterInput.loadOptionsError": "Error fetching options", + "parameterInput.loadOptionsCredentialsRequired": "Set up credential to see options", "parameterInput.openEditWindow": "Open Edit Window", "parameterInput.parameter": "Parameter: \"{shortPath}\"", "parameterInput.parameterHasExpression": "Parameter: \"{shortPath}\" has an expression", From 262693be653412f1917f911ebb719e6da6d94829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 26 Sep 2024 12:16:10 +0200 Subject: [PATCH 002/178] chore: Stop reporting `AxiosError` to Sentry (#10974) --- packages/cli/src/error-reporting.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/cli/src/error-reporting.ts b/packages/cli/src/error-reporting.ts index d1ecd39198..e429bdbd30 100644 --- a/packages/cli/src/error-reporting.ts +++ b/packages/cli/src/error-reporting.ts @@ -1,6 +1,7 @@ import { GlobalConfig } from '@n8n/config'; // eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import import { QueryFailedError } from '@n8n/typeorm'; +import { AxiosError } from 'axios'; import { createHash } from 'crypto'; import { ErrorReporterProxy, ApplicationError } from 'n8n-workflow'; import Container from 'typedi'; @@ -67,6 +68,8 @@ export const initErrorHandling = async () => { beforeSend(event, { originalException }) { if (!originalException) return null; + if (originalException instanceof AxiosError) return null; + if ( originalException instanceof QueryFailedError && ['SQLITE_FULL', 'SQLITE_IOERR'].some((errMsg) => originalException.message.includes(errMsg)) From efc5d859eb384c40826180786ea024b3ebcf82b6 Mon Sep 17 00:00:00 2001 From: Eugene Date: Thu, 26 Sep 2024 13:15:08 +0200 Subject: [PATCH 003/178] chore: Correct naming of annotation-related files (#10946) --- packages/cli/.eslintrc.js | 6 ++++++ ...roller.ts => annotation-tags.controller.ee.ts} | 2 +- ...-tag-entity.ts => annotation-tag-entity.ee.ts} | 4 ++-- ...ag-mapping.ts => annotation-tag-mapping.ee.ts} | 4 ++-- ...n-annotation.ts => execution-annotation.ee.ts} | 4 ++-- .../src/databases/entities/execution-entity.ts | 2 +- packages/cli/src/databases/entities/index.ts | 6 +++--- ...ts => annotation-tag-mapping.repository.ee.ts} | 2 +- ...ository.ts => annotation-tag.repository.ee.ts} | 2 +- .../execution-annotation.repository.ts | 2 +- .../repositories/execution.repository.ts | 6 +++--- packages/cli/src/executions/execution.service.ts | 2 +- packages/cli/src/generic-helpers.ts | 2 +- packages/cli/src/interfaces.ts | 2 +- packages/cli/src/server.ts | 2 +- ...ag.service.ts => annotation-tag.service.ee.ts} | 4 ++-- .../cli/test/integration/shared/db/executions.ts | 2 +- packages/cli/test/integration/shared/test-db.ts | 15 ++++++++++++--- .../test/integration/shared/utils/test-server.ts | 2 +- ...ntainer.vue => AnnotationTagsContainer.ee.vue} | 0 ...Dropdown.vue => AnnotationTagsDropdown.ee.vue} | 0 packages/editor-ui/src/components/Modals.vue | 2 +- ...gsManager.vue => AnnotationTagsManager.ee.vue} | 0 .../components/executions/ExecutionsFilter.vue | 2 +- ...ue => WorkflowExecutionAnnotationPanel.ee.vue} | 2 +- 25 files changed, 46 insertions(+), 31 deletions(-) rename packages/cli/src/controllers/{annotation-tags.controller.ts => annotation-tags.controller.ee.ts} (99%) rename packages/cli/src/databases/entities/{annotation-tag-entity.ts => annotation-tag-entity.ee.ts} (93%) rename packages/cli/src/databases/entities/{annotation-tag-mapping.ts => annotation-tag-mapping.ee.ts} (98%) rename packages/cli/src/databases/entities/{execution-annotation.ts => execution-annotation.ee.ts} (98%) rename packages/cli/src/databases/repositories/{annotation-tag-mapping.repository.ts => annotation-tag-mapping.repository.ee.ts} (97%) rename packages/cli/src/databases/repositories/{annotation-tag.repository.ts => annotation-tag.repository.ee.ts} (94%) rename packages/cli/src/services/{annotation-tag.service.ts => annotation-tag.service.ee.ts} (96%) rename packages/editor-ui/src/components/{AnnotationTagsContainer.vue => AnnotationTagsContainer.ee.vue} (100%) rename packages/editor-ui/src/components/{AnnotationTagsDropdown.vue => AnnotationTagsDropdown.ee.vue} (100%) rename packages/editor-ui/src/components/TagsManager/{AnnotationTagsManager.vue => AnnotationTagsManager.ee.vue} (100%) rename packages/editor-ui/src/components/executions/workflow/{WorkflowExecutionAnnotationPanel.vue => WorkflowExecutionAnnotationPanel.ee.vue} (99%) diff --git a/packages/cli/.eslintrc.js b/packages/cli/.eslintrc.js index a3a1e612ab..6002da5caa 100644 --- a/packages/cli/.eslintrc.js +++ b/packages/cli/.eslintrc.js @@ -71,5 +71,11 @@ module.exports = { ], }, }, + { + files: ['./test/**/*.ts', './src/**/__tests__/**/*.ts'], + rules: { + 'n8n-local-rules/no-dynamic-import-template': 'off', + }, + }, ], }; diff --git a/packages/cli/src/controllers/annotation-tags.controller.ts b/packages/cli/src/controllers/annotation-tags.controller.ee.ts similarity index 99% rename from packages/cli/src/controllers/annotation-tags.controller.ts rename to packages/cli/src/controllers/annotation-tags.controller.ee.ts index ff43c2ef7e..ed6ff8c52a 100644 --- a/packages/cli/src/controllers/annotation-tags.controller.ts +++ b/packages/cli/src/controllers/annotation-tags.controller.ee.ts @@ -1,6 +1,6 @@ import { Delete, Get, Patch, Post, RestController, GlobalScope } from '@/decorators'; import { AnnotationTagsRequest } from '@/requests'; -import { AnnotationTagService } from '@/services/annotation-tag.service'; +import { AnnotationTagService } from '@/services/annotation-tag.service.ee'; @RestController('/annotation-tags') export class AnnotationTagsController { diff --git a/packages/cli/src/databases/entities/annotation-tag-entity.ts b/packages/cli/src/databases/entities/annotation-tag-entity.ee.ts similarity index 93% rename from packages/cli/src/databases/entities/annotation-tag-entity.ts rename to packages/cli/src/databases/entities/annotation-tag-entity.ee.ts index e89c75a5a8..4aed8df8af 100644 --- a/packages/cli/src/databases/entities/annotation-tag-entity.ts +++ b/packages/cli/src/databases/entities/annotation-tag-entity.ee.ts @@ -1,8 +1,8 @@ import { Column, Entity, Index, ManyToMany, OneToMany } from '@n8n/typeorm'; import { IsString, Length } from 'class-validator'; -import type { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping'; -import type { ExecutionAnnotation } from '@/databases/entities/execution-annotation'; +import type { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping.ee'; +import type { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee'; import { WithTimestampsAndStringId } from './abstract-entity'; diff --git a/packages/cli/src/databases/entities/annotation-tag-mapping.ts b/packages/cli/src/databases/entities/annotation-tag-mapping.ee.ts similarity index 98% rename from packages/cli/src/databases/entities/annotation-tag-mapping.ts rename to packages/cli/src/databases/entities/annotation-tag-mapping.ee.ts index 2fa4fc79c1..5b1b9e1bf2 100644 --- a/packages/cli/src/databases/entities/annotation-tag-mapping.ts +++ b/packages/cli/src/databases/entities/annotation-tag-mapping.ee.ts @@ -1,7 +1,7 @@ import { Entity, JoinColumn, ManyToOne, PrimaryColumn } from '@n8n/typeorm'; -import type { AnnotationTagEntity } from './annotation-tag-entity'; -import type { ExecutionAnnotation } from './execution-annotation'; +import type { AnnotationTagEntity } from './annotation-tag-entity.ee'; +import type { ExecutionAnnotation } from './execution-annotation.ee'; /** * This entity represents the junction table between the execution annotations and the tags diff --git a/packages/cli/src/databases/entities/execution-annotation.ts b/packages/cli/src/databases/entities/execution-annotation.ee.ts similarity index 98% rename from packages/cli/src/databases/entities/execution-annotation.ts rename to packages/cli/src/databases/entities/execution-annotation.ee.ts index 33943f6330..59820c83b5 100644 --- a/packages/cli/src/databases/entities/execution-annotation.ts +++ b/packages/cli/src/databases/entities/execution-annotation.ee.ts @@ -12,8 +12,8 @@ import { } from '@n8n/typeorm'; import type { AnnotationVote } from 'n8n-workflow'; -import type { AnnotationTagEntity } from './annotation-tag-entity'; -import type { AnnotationTagMapping } from './annotation-tag-mapping'; +import type { AnnotationTagEntity } from './annotation-tag-entity.ee'; +import type { AnnotationTagMapping } from './annotation-tag-mapping.ee'; import { ExecutionEntity } from './execution-entity'; @Entity({ name: 'execution_annotations' }) diff --git a/packages/cli/src/databases/entities/execution-entity.ts b/packages/cli/src/databases/entities/execution-entity.ts index d44cb6c3f0..f481bb97f4 100644 --- a/packages/cli/src/databases/entities/execution-entity.ts +++ b/packages/cli/src/databases/entities/execution-entity.ts @@ -12,7 +12,7 @@ import { } from '@n8n/typeorm'; import { ExecutionStatus, WorkflowExecuteMode } from 'n8n-workflow'; -import type { ExecutionAnnotation } from '@/databases/entities/execution-annotation'; +import type { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee'; import { datetimeColumnType } from './abstract-entity'; import type { ExecutionData } from './execution-data'; diff --git a/packages/cli/src/databases/entities/index.ts b/packages/cli/src/databases/entities/index.ts index 8ae29ababd..383ad4084d 100644 --- a/packages/cli/src/databases/entities/index.ts +++ b/packages/cli/src/databases/entities/index.ts @@ -1,11 +1,11 @@ -import { AnnotationTagEntity } from './annotation-tag-entity'; -import { AnnotationTagMapping } from './annotation-tag-mapping'; +import { AnnotationTagEntity } from './annotation-tag-entity.ee'; +import { AnnotationTagMapping } from './annotation-tag-mapping.ee'; import { AuthIdentity } from './auth-identity'; import { AuthProviderSyncHistory } from './auth-provider-sync-history'; import { AuthUser } from './auth-user'; import { CredentialsEntity } from './credentials-entity'; import { EventDestinations } from './event-destinations'; -import { ExecutionAnnotation } from './execution-annotation'; +import { ExecutionAnnotation } from './execution-annotation.ee'; import { ExecutionData } from './execution-data'; import { ExecutionEntity } from './execution-entity'; import { ExecutionMetadata } from './execution-metadata'; diff --git a/packages/cli/src/databases/repositories/annotation-tag-mapping.repository.ts b/packages/cli/src/databases/repositories/annotation-tag-mapping.repository.ee.ts similarity index 97% rename from packages/cli/src/databases/repositories/annotation-tag-mapping.repository.ts rename to packages/cli/src/databases/repositories/annotation-tag-mapping.repository.ee.ts index c8c4a80d31..07bb79815b 100644 --- a/packages/cli/src/databases/repositories/annotation-tag-mapping.repository.ts +++ b/packages/cli/src/databases/repositories/annotation-tag-mapping.repository.ee.ts @@ -1,7 +1,7 @@ import { DataSource, Repository } from '@n8n/typeorm'; import { Service } from 'typedi'; -import { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping'; +import { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping.ee'; @Service() export class AnnotationTagMappingRepository extends Repository { diff --git a/packages/cli/src/databases/repositories/annotation-tag.repository.ts b/packages/cli/src/databases/repositories/annotation-tag.repository.ee.ts similarity index 94% rename from packages/cli/src/databases/repositories/annotation-tag.repository.ts rename to packages/cli/src/databases/repositories/annotation-tag.repository.ee.ts index 2f4b847ba6..e3aa993460 100644 --- a/packages/cli/src/databases/repositories/annotation-tag.repository.ts +++ b/packages/cli/src/databases/repositories/annotation-tag.repository.ee.ts @@ -1,7 +1,7 @@ import { DataSource, Repository } from '@n8n/typeorm'; import { Service } from 'typedi'; -import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity'; +import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee'; @Service() export class AnnotationTagRepository extends Repository { diff --git a/packages/cli/src/databases/repositories/execution-annotation.repository.ts b/packages/cli/src/databases/repositories/execution-annotation.repository.ts index 81d4917173..97ca972733 100644 --- a/packages/cli/src/databases/repositories/execution-annotation.repository.ts +++ b/packages/cli/src/databases/repositories/execution-annotation.repository.ts @@ -1,7 +1,7 @@ import { DataSource, Repository } from '@n8n/typeorm'; import { Service } from 'typedi'; -import { ExecutionAnnotation } from '@/databases/entities/execution-annotation'; +import { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee'; @Service() export class ExecutionAnnotationRepository extends Repository { diff --git a/packages/cli/src/databases/repositories/execution.repository.ts b/packages/cli/src/databases/repositories/execution.repository.ts index 52c7fd65f3..b99bd8adee 100644 --- a/packages/cli/src/databases/repositories/execution.repository.ts +++ b/packages/cli/src/databases/repositories/execution.repository.ts @@ -36,9 +36,9 @@ import type { import { Service } from 'typedi'; import config from '@/config'; -import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity'; -import { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping'; -import { ExecutionAnnotation } from '@/databases/entities/execution-annotation'; +import { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee'; +import { AnnotationTagMapping } from '@/databases/entities/annotation-tag-mapping.ee'; +import { ExecutionAnnotation } from '@/databases/entities/execution-annotation.ee'; import { PostgresLiveRowsRetrievalError } from '@/errors/postgres-live-rows-retrieval.error'; import type { ExecutionSummaries } from '@/executions/execution.types'; import type { diff --git a/packages/cli/src/executions/execution.service.ts b/packages/cli/src/executions/execution.service.ts index a04c10e45f..53023fce9a 100644 --- a/packages/cli/src/executions/execution.service.ts +++ b/packages/cli/src/executions/execution.service.ts @@ -21,7 +21,7 @@ import { ActiveExecutions } from '@/active-executions'; import { ConcurrencyControlService } from '@/concurrency/concurrency-control.service'; import config from '@/config'; import type { User } from '@/databases/entities/user'; -import { AnnotationTagMappingRepository } from '@/databases/repositories/annotation-tag-mapping.repository'; +import { AnnotationTagMappingRepository } from '@/databases/repositories/annotation-tag-mapping.repository.ee'; import { ExecutionAnnotationRepository } from '@/databases/repositories/execution-annotation.repository'; import { ExecutionRepository } from '@/databases/repositories/execution.repository'; import type { IGetExecutionsQueryFilter } from '@/databases/repositories/execution.repository'; diff --git a/packages/cli/src/generic-helpers.ts b/packages/cli/src/generic-helpers.ts index 47ef1a796b..e5978bb34a 100644 --- a/packages/cli/src/generic-helpers.ts +++ b/packages/cli/src/generic-helpers.ts @@ -1,6 +1,6 @@ import { validate } from 'class-validator'; -import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity'; +import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee'; import type { CredentialsEntity } from '@/databases/entities/credentials-entity'; import type { TagEntity } from '@/databases/entities/tag-entity'; import type { User } from '@/databases/entities/user'; diff --git a/packages/cli/src/interfaces.ts b/packages/cli/src/interfaces.ts index 629925e8dc..4d767862bb 100644 --- a/packages/cli/src/interfaces.ts +++ b/packages/cli/src/interfaces.ts @@ -26,7 +26,7 @@ import type { import type PCancelable from 'p-cancelable'; import type { ActiveWorkflowManager } from '@/active-workflow-manager'; -import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity'; +import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee'; import type { AuthProviderType } from '@/databases/entities/auth-identity'; import type { SharedCredentials } from '@/databases/entities/shared-credentials'; import type { TagEntity } from '@/databases/entities/tag-entity'; diff --git a/packages/cli/src/server.ts b/packages/cli/src/server.ts index 27ac3b09a1..0840714b6a 100644 --- a/packages/cli/src/server.ts +++ b/packages/cli/src/server.ts @@ -35,7 +35,7 @@ import type { FrontendService } from '@/services/frontend.service'; import { OrchestrationService } from '@/services/orchestration.service'; import '@/controllers/active-workflows.controller'; -import '@/controllers/annotation-tags.controller'; +import '@/controllers/annotation-tags.controller.ee'; import '@/controllers/auth.controller'; import '@/controllers/binary-data.controller'; import '@/controllers/curl.controller'; diff --git a/packages/cli/src/services/annotation-tag.service.ts b/packages/cli/src/services/annotation-tag.service.ee.ts similarity index 96% rename from packages/cli/src/services/annotation-tag.service.ts rename to packages/cli/src/services/annotation-tag.service.ee.ts index 27c93041b5..671395168c 100644 --- a/packages/cli/src/services/annotation-tag.service.ts +++ b/packages/cli/src/services/annotation-tag.service.ee.ts @@ -1,7 +1,7 @@ import { Service } from 'typedi'; -import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity'; -import { AnnotationTagRepository } from '@/databases/repositories/annotation-tag.repository'; +import type { AnnotationTagEntity } from '@/databases/entities/annotation-tag-entity.ee'; +import { AnnotationTagRepository } from '@/databases/repositories/annotation-tag.repository.ee'; import { validateEntity } from '@/generic-helpers'; import type { IAnnotationTagDb, IAnnotationTagWithCountDb } from '@/interfaces'; diff --git a/packages/cli/test/integration/shared/db/executions.ts b/packages/cli/test/integration/shared/db/executions.ts index dac3124681..e09ca44dfb 100644 --- a/packages/cli/test/integration/shared/db/executions.ts +++ b/packages/cli/test/integration/shared/db/executions.ts @@ -4,7 +4,7 @@ import Container from 'typedi'; import type { ExecutionData } from '@/databases/entities/execution-data'; import type { ExecutionEntity } from '@/databases/entities/execution-entity'; import type { WorkflowEntity } from '@/databases/entities/workflow-entity'; -import { AnnotationTagRepository } from '@/databases/repositories/annotation-tag.repository'; +import { AnnotationTagRepository } from '@/databases/repositories/annotation-tag.repository.ee'; import { ExecutionDataRepository } from '@/databases/repositories/execution-data.repository'; import { ExecutionMetadataRepository } from '@/databases/repositories/execution-metadata.repository'; import { ExecutionRepository } from '@/databases/repositories/execution.repository'; diff --git a/packages/cli/test/integration/shared/test-db.ts b/packages/cli/test/integration/shared/test-db.ts index 365bc81fa8..f899cc7e90 100644 --- a/packages/cli/test/integration/shared/test-db.ts +++ b/packages/cli/test/integration/shared/test-db.ts @@ -87,9 +87,18 @@ const repositories = [ */ export async function truncate(names: Array<(typeof repositories)[number]>) { for (const name of names) { - const RepositoryClass: Class> = - // eslint-disable-next-line n8n-local-rules/no-dynamic-import-template - (await import(`@/databases/repositories/${kebabCase(name)}.repository`))[`${name}Repository`]; + let RepositoryClass: Class>; + + try { + RepositoryClass = (await import(`@/databases/repositories/${kebabCase(name)}.repository`))[ + `${name}Repository` + ]; + } catch (e) { + RepositoryClass = (await import(`@/databases/repositories/${kebabCase(name)}.repository.ee`))[ + `${name}Repository` + ]; + } + await Container.get(RepositoryClass).delete({}); } } diff --git a/packages/cli/test/integration/shared/utils/test-server.ts b/packages/cli/test/integration/shared/utils/test-server.ts index f16bbf9833..e54a88fbdc 100644 --- a/packages/cli/test/integration/shared/utils/test-server.ts +++ b/packages/cli/test/integration/shared/utils/test-server.ts @@ -140,7 +140,7 @@ export const setupTestServer = ({ for (const group of endpointGroups) { switch (group) { case 'annotationTags': - await import('@/controllers/annotation-tags.controller'); + await import('@/controllers/annotation-tags.controller.ee'); break; case 'credentials': diff --git a/packages/editor-ui/src/components/AnnotationTagsContainer.vue b/packages/editor-ui/src/components/AnnotationTagsContainer.ee.vue similarity index 100% rename from packages/editor-ui/src/components/AnnotationTagsContainer.vue rename to packages/editor-ui/src/components/AnnotationTagsContainer.ee.vue diff --git a/packages/editor-ui/src/components/AnnotationTagsDropdown.vue b/packages/editor-ui/src/components/AnnotationTagsDropdown.ee.vue similarity index 100% rename from packages/editor-ui/src/components/AnnotationTagsDropdown.vue rename to packages/editor-ui/src/components/AnnotationTagsDropdown.ee.vue diff --git a/packages/editor-ui/src/components/Modals.vue b/packages/editor-ui/src/components/Modals.vue index a29e6c0779..396e70ce02 100644 --- a/packages/editor-ui/src/components/Modals.vue +++ b/packages/editor-ui/src/components/Modals.vue @@ -48,7 +48,7 @@ import DuplicateWorkflowDialog from '@/components/DuplicateWorkflowDialog.vue'; import ModalRoot from '@/components/ModalRoot.vue'; import PersonalizationModal from '@/components/PersonalizationModal.vue'; import WorkflowTagsManager from '@/components/TagsManager/WorkflowTagsManager.vue'; -import AnnotationTagsManager from '@/components/TagsManager/AnnotationTagsManager.vue'; +import AnnotationTagsManager from '@/components/TagsManager/AnnotationTagsManager.ee.vue'; import UpdatesPanel from '@/components/UpdatesPanel.vue'; import NpsSurvey from '@/components/NpsSurvey.vue'; import WorkflowLMChat from '@/components/WorkflowLMChat/WorkflowLMChat.vue'; diff --git a/packages/editor-ui/src/components/TagsManager/AnnotationTagsManager.vue b/packages/editor-ui/src/components/TagsManager/AnnotationTagsManager.ee.vue similarity index 100% rename from packages/editor-ui/src/components/TagsManager/AnnotationTagsManager.vue rename to packages/editor-ui/src/components/TagsManager/AnnotationTagsManager.ee.vue diff --git a/packages/editor-ui/src/components/executions/ExecutionsFilter.vue b/packages/editor-ui/src/components/executions/ExecutionsFilter.vue index f4ac47c595..08afe7cceb 100644 --- a/packages/editor-ui/src/components/executions/ExecutionsFilter.vue +++ b/packages/editor-ui/src/components/executions/ExecutionsFilter.vue @@ -15,7 +15,7 @@ import { usePostHog } from '@/stores/posthog.store'; import { useTelemetry } from '@/composables/useTelemetry'; import type { Placement } from '@floating-ui/core'; import { useDebounce } from '@/composables/useDebounce'; -import AnnotationTagsDropdown from '@/components/AnnotationTagsDropdown.vue'; +import AnnotationTagsDropdown from '@/components/AnnotationTagsDropdown.ee.vue'; export type ExecutionFilterProps = { workflows?: Array; diff --git a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.ee.vue similarity index 99% rename from packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue rename to packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.ee.vue index 437cfd8e12..424c38aad8 100644 --- a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.vue +++ b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionAnnotationPanel.ee.vue @@ -2,7 +2,7 @@ import { ref, computed } from 'vue'; import type { AnnotationVote, ExecutionSummary } from 'n8n-workflow'; import { useExecutionsStore } from '@/stores/executions.store'; -import AnnotationTagsDropdown from '@/components/AnnotationTagsDropdown.vue'; +import AnnotationTagsDropdown from '@/components/AnnotationTagsDropdown.ee.vue'; import { createEventBus } from 'n8n-design-system'; import VoteButtons from '@/components/executions/workflow/VoteButtons.vue'; import { useToast } from '@/composables/useToast'; From 5a99e93f8d2c66d7dbcef382478badd63bc4a0b5 Mon Sep 17 00:00:00 2001 From: Eugene Date: Thu, 26 Sep 2024 13:15:50 +0200 Subject: [PATCH 004/178] fix(HTTP Request Tool Node): Remove default user agent header (#10971) --- .../nodes/tools/ToolHttpRequest/ToolHttpRequest.node.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/ToolHttpRequest.node.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/ToolHttpRequest.node.ts index 421e85e1b5..32f6be42e7 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/ToolHttpRequest.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolHttpRequest/ToolHttpRequest.node.ts @@ -275,7 +275,11 @@ export class ToolHttpRequest implements INodeType { method: this.getNodeParameter('method', itemIndex, 'GET') as IHttpRequestMethods, url: this.getNodeParameter('url', itemIndex) as string, qs: {}, - headers: {}, + headers: { + // FIXME: This is a workaround to prevent the node from sending a default User-Agent (`n8n`) when the header is not set. + // Needs to be replaced with a proper fix after NODE-1777 is resolved + 'User-Agent': undefined, + }, body: {}, }; From 06d749ffa7ced503141d8b07e22c47d971eb1623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 26 Sep 2024 13:31:34 +0200 Subject: [PATCH 005/178] feat(core): Filter executions by project ID in internal API (#10976) --- .../repositories/execution.repository.ts | 9 ++++++ .../cli/src/executions/execution.types.ts | 1 + .../execution.service.integration.test.ts | 32 +++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/packages/cli/src/databases/repositories/execution.repository.ts b/packages/cli/src/databases/repositories/execution.repository.ts index b99bd8adee..d76d78c99a 100644 --- a/packages/cli/src/databases/repositories/execution.repository.ts +++ b/packages/cli/src/databases/repositories/execution.repository.ts @@ -54,6 +54,8 @@ import { ExecutionDataRepository } from './execution-data.repository'; import type { ExecutionData } from '../entities/execution-data'; import { ExecutionEntity } from '../entities/execution-entity'; import { ExecutionMetadata } from '../entities/execution-metadata'; +import { SharedWorkflow } from '../entities/shared-workflow'; +import { WorkflowEntity } from '../entities/workflow-entity'; export interface IGetExecutionsQueryFilter { id?: FindOperator | string; @@ -874,6 +876,7 @@ export class ExecutionRepository extends Repository { metadata, annotationTags, vote, + projectId, } = query; const fields = Object.keys(this.summaryFields) @@ -945,6 +948,12 @@ export class ExecutionRepository extends Repository { } } + if (projectId) { + qb.innerJoin(WorkflowEntity, 'w', 'w.id = execution.workflowId') + .innerJoin(SharedWorkflow, 'sw', 'sw.workflowId = w.id') + .where('sw.projectId = :projectId', { projectId }); + } + return qb; } diff --git a/packages/cli/src/executions/execution.types.ts b/packages/cli/src/executions/execution.types.ts index ba574199a0..04d68d8197 100644 --- a/packages/cli/src/executions/execution.types.ts +++ b/packages/cli/src/executions/execution.types.ts @@ -80,6 +80,7 @@ export namespace ExecutionSummaries { startedBefore: string; annotationTags: string[]; // tag IDs vote: AnnotationVote; + projectId: string; }>; type AccessFields = { diff --git a/packages/cli/test/integration/execution.service.integration.test.ts b/packages/cli/test/integration/execution.service.integration.test.ts index 05061e536e..15d97f69ab 100644 --- a/packages/cli/test/integration/execution.service.integration.test.ts +++ b/packages/cli/test/integration/execution.service.integration.test.ts @@ -6,6 +6,7 @@ import { ExecutionRepository } from '@/databases/repositories/execution.reposito import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { ExecutionService } from '@/executions/execution.service'; import type { ExecutionSummaries } from '@/executions/execution.types'; +import { createTeamProject } from '@test-integration/db/projects'; import { annotateExecution, createAnnotationTags, createExecution } from './shared/db/executions'; import { createWorkflow } from './shared/db/workflows'; @@ -294,6 +295,37 @@ describe('ExecutionService', () => { }); }); + test('should filter executions by `projectId`', async () => { + const firstProject = await createTeamProject(); + const secondProject = await createTeamProject(); + + const firstWorkflow = await createWorkflow(undefined, firstProject); + const secondWorkflow = await createWorkflow(undefined, secondProject); + + await createExecution({ status: 'success' }, firstWorkflow); + await createExecution({ status: 'success' }, firstWorkflow); + await createExecution({ status: 'success' }, secondWorkflow); // to filter out + + const query: ExecutionSummaries.RangeQuery = { + kind: 'range', + range: { limit: 20 }, + accessibleWorkflowIds: [firstWorkflow.id], + projectId: firstProject.id, + }; + + const output = await executionService.findRangeWithCount(query); + + expect(output).toEqual({ + count: 2, + estimated: false, + results: expect.arrayContaining([ + expect.objectContaining({ workflowId: firstWorkflow.id }), + expect.objectContaining({ workflowId: firstWorkflow.id }), + // execution for workflow in second project was filtered out + ]), + }); + }); + test('should exclude executions by inaccessible `workflowId`', async () => { const accessibleWorkflow = await createWorkflow(); const inaccessibleWorkflow = await createWorkflow(); From b7951a071c7ab7f68e78812043f9ec90c02f39ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Thu, 26 Sep 2024 14:15:11 +0200 Subject: [PATCH 006/178] refactor(editor): Convert Sticky component to Options API (no-changelog) (#10975) --- .../src/components/N8nMarkdown/Markdown.vue | 4 +- .../N8nResizeableSticky/ResizeableSticky.vue | 7 +- .../src/components/N8nSticky/Sticky.vue | 4 +- packages/editor-ui/src/components/Sticky.vue | 580 ++++++++---------- 4 files changed, 280 insertions(+), 315 deletions(-) diff --git a/packages/design-system/src/components/N8nMarkdown/Markdown.vue b/packages/design-system/src/components/N8nMarkdown/Markdown.vue index 7f55c0c6f1..1074d584bf 100644 --- a/packages/design-system/src/components/N8nMarkdown/Markdown.vue +++ b/packages/design-system/src/components/N8nMarkdown/Markdown.vue @@ -136,7 +136,7 @@ const htmlContent = computed(() => { }); const emit = defineEmits<{ - 'markdown-click': [link: string, e: MouseEvent]; + 'markdown-click': [link: HTMLAnchorElement, e: MouseEvent]; 'update-content': [content: string]; }>(); @@ -154,7 +154,7 @@ const onClick = (event: MouseEvent) => { } } if (clickedLink) { - emit('markdown-click', clickedLink?.href, event); + emit('markdown-click', clickedLink, event); } }; diff --git a/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.vue b/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.vue index ea72c506b7..5219efe2f4 100644 --- a/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.vue +++ b/packages/design-system/src/components/N8nResizeableSticky/ResizeableSticky.vue @@ -21,6 +21,7 @@ const emit = defineEmits<{ resize: [values: ResizeData]; resizestart: []; resizeend: []; + 'markdown-click': [link: HTMLAnchorElement, e: MouseEvent]; }>(); const attrs = useAttrs(); @@ -42,6 +43,10 @@ const onResizeEnd = () => { isResizing.value = false; emit('resizeend'); }; + +const onMarkdownClick = (link: HTMLAnchorElement, event: MouseEvent) => { + emit('markdown-click', link, event); +}; diff --git a/packages/design-system/src/components/N8nSticky/Sticky.vue b/packages/design-system/src/components/N8nSticky/Sticky.vue index 73aa4933a3..569cd15974 100644 --- a/packages/design-system/src/components/N8nSticky/Sticky.vue +++ b/packages/design-system/src/components/N8nSticky/Sticky.vue @@ -13,7 +13,7 @@ const props = withDefaults(defineProps(), defaultStickyProps); const emit = defineEmits<{ edit: [editing: boolean]; 'update:modelValue': [value: string]; - 'markdown-click': [link: string, e: Event]; + 'markdown-click': [link: HTMLAnchorElement, e: MouseEvent]; }>(); const { t } = useI18n(); @@ -63,7 +63,7 @@ const onUpdateModelValue = (value: string) => { emit('update:modelValue', value); }; -const onMarkdownClick = (link: string, event: Event) => { +const onMarkdownClick = (link: HTMLAnchorElement, event: MouseEvent) => { emit('markdown-click', link, event); }; diff --git a/packages/editor-ui/src/components/Sticky.vue b/packages/editor-ui/src/components/Sticky.vue index 9888d6253d..0d11628901 100644 --- a/packages/editor-ui/src/components/Sticky.vue +++ b/packages/editor-ui/src/components/Sticky.vue @@ -1,19 +1,12 @@ - - - -