mirror of
https://github.com/n8n-io/n8n.git
synced 2026-08-28 17:22:01 +08:00
feat(core): Add project Git connection table (no-changelog) (#36549)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+9
@@ -64,6 +64,7 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand.
|
||||
| [public.folder](public.folder.md) | 6 | | BASE TABLE |
|
||||
| [public.folder_tag](public.folder_tag.md) | 2 | | BASE TABLE |
|
||||
| [public.git_connection](public.git_connection.md) | 13 | | BASE TABLE |
|
||||
| [public.git_connection_project](public.git_connection_project.md) | 4 | | BASE TABLE |
|
||||
| [public.insights_by_period](public.insights_by_period.md) | 6 | | BASE TABLE |
|
||||
| [public.insights_metadata](public.insights_metadata.md) | 5 | | BASE TABLE |
|
||||
| [public.insights_raw](public.insights_raw.md) | 5 | | BASE TABLE |
|
||||
@@ -248,6 +249,8 @@ erDiagram
|
||||
"public.folder" }o--o| "public.folder" : "FOREIGN KEY (#quot;parentFolderId#quot;) REFERENCES folder(id) ON DELETE CASCADE"
|
||||
"public.folder_tag" }o--|| "public.tag_entity" : "FOREIGN KEY (#quot;tagId#quot;) REFERENCES tag_entity(id) ON DELETE CASCADE"
|
||||
"public.folder_tag" }o--|| "public.folder" : "FOREIGN KEY (#quot;folderId#quot;) REFERENCES folder(id) ON DELETE CASCADE"
|
||||
"public.git_connection_project" |o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.git_connection_project" }o--|| "public.git_connection" : "FOREIGN KEY (#quot;gitConnectionId#quot;) REFERENCES git_connection(id) ON DELETE CASCADE"
|
||||
"public.insights_by_period" }o--|| "public.insights_metadata" : "FOREIGN KEY (#quot;metaId#quot;) REFERENCES insights_metadata(#quot;metaId#quot;) ON DELETE CASCADE"
|
||||
"public.insights_metadata" }o--o| "public.workflow_entity" : "FOREIGN KEY (#quot;workflowId#quot;) REFERENCES workflow_entity(id) ON DELETE SET NULL"
|
||||
"public.insights_metadata" }o--o| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE SET NULL"
|
||||
@@ -925,6 +928,12 @@ erDiagram
|
||||
text repositoryUrl
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.git_connection_project" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ gitConnectionId FK
|
||||
varchar_36_ projectId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.insights_by_period" {
|
||||
integer id
|
||||
integer metaId FK
|
||||
|
||||
+8
-1
@@ -11,7 +11,7 @@
|
||||
| encryptedPassword | text | | true | | | Encrypted HTTPS password/token; set when connectionType is 'https', null for 'ssh'. |
|
||||
| encryptedPrivateKey | text | | true | | | Encrypted SSH private key; set when connectionType is 'ssh', null for 'https'. |
|
||||
| encryptedUsername | text | | true | | | Encrypted HTTPS username; set when connectionType is 'https', null for 'ssh'. |
|
||||
| id | varchar(36) | | false | | | |
|
||||
| id | varchar(36) | | false | [public.git_connection_project](public.git_connection_project.md) | | |
|
||||
| keyGeneratorType | varchar(16) | | true | | | GitKeyGeneratorType enum: "ed25519", "rsa" |
|
||||
| name | varchar(128) | | false | | | |
|
||||
| publicKey | text | | true | | | SSH public key; set when connectionType is 'ssh', null for 'https'. |
|
||||
@@ -45,6 +45,7 @@
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"public.git_connection_project" }o--|| "public.git_connection" : "FOREIGN KEY (#quot;gitConnectionId#quot;) REFERENCES git_connection(id) ON DELETE CASCADE"
|
||||
|
||||
"public.git_connection" {
|
||||
varchar_64_ baseCommit
|
||||
@@ -61,6 +62,12 @@ erDiagram
|
||||
text repositoryUrl
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.git_connection_project" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ gitConnectionId FK
|
||||
varchar_36_ projectId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# public.git_connection_project
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
| gitConnectionId | varchar(36) | | false | | [public.git_connection](public.git_connection.md) | |
|
||||
| projectId | varchar(36) | | false | | [public.project](public.project.md) | |
|
||||
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| FK_12a7fe192a1ee1d933e712e6b1c | FOREIGN KEY | FOREIGN KEY ("gitConnectionId") REFERENCES git_connection(id) ON DELETE CASCADE |
|
||||
| FK_5affe1c6075bd7ac20297b7947c | FOREIGN KEY | FOREIGN KEY ("projectId") REFERENCES project(id) ON DELETE CASCADE |
|
||||
| PK_5affe1c6075bd7ac20297b7947c | PRIMARY KEY | PRIMARY KEY ("projectId") |
|
||||
| git_connection_project_createdAt_not_null | n | NOT NULL "createdAt" |
|
||||
| git_connection_project_gitConnectionId_not_null | n | NOT NULL "gitConnectionId" |
|
||||
| git_connection_project_projectId_not_null | n | NOT NULL "projectId" |
|
||||
| git_connection_project_updatedAt_not_null | n | NOT NULL "updatedAt" |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| IDX_12a7fe192a1ee1d933e712e6b1 | CREATE INDEX "IDX_12a7fe192a1ee1d933e712e6b1" ON public.git_connection_project USING btree ("gitConnectionId") |
|
||||
| PK_5affe1c6075bd7ac20297b7947c | CREATE UNIQUE INDEX "PK_5affe1c6075bd7ac20297b7947c" ON public.git_connection_project USING btree ("projectId") |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"public.git_connection_project" }o--|| "public.git_connection" : "FOREIGN KEY (#quot;gitConnectionId#quot;) REFERENCES git_connection(id) ON DELETE CASCADE"
|
||||
"public.git_connection_project" |o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
|
||||
"public.git_connection_project" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ gitConnectionId FK
|
||||
varchar_36_ projectId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.git_connection" {
|
||||
varchar_64_ baseCommit
|
||||
varchar_255_ branchName
|
||||
varchar_16_ connectionType
|
||||
timestamp_3__with_time_zone createdAt
|
||||
text encryptedPassword
|
||||
text encryptedPrivateKey
|
||||
text encryptedUsername
|
||||
varchar_36_ id
|
||||
varchar_16_ keyGeneratorType
|
||||
varchar_128_ name
|
||||
text publicKey
|
||||
text repositoryUrl
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.project" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
uuid creatorId FK
|
||||
json customTelemetryTags
|
||||
varchar_512_ description
|
||||
json icon
|
||||
varchar_36_ id
|
||||
varchar_255_ name
|
||||
varchar_36_ type
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
+8
-1
@@ -9,7 +9,7 @@
|
||||
| customTelemetryTags | json | '[]'::json | false | | | |
|
||||
| description | varchar(512) | | true | | | |
|
||||
| icon | json | | true | | | |
|
||||
| id | varchar(36) | | false | [public.agent_chat_attachments](public.agent_chat_attachments.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agents](public.agents.md) [public.data_table](public.data_table.md) [public.folder](public.folder.md) [public.insights_metadata](public.insights_metadata.md) [public.instance_ai_threads](public.instance_ai_threads.md) [public.project_relation](public.project_relation.md) [public.project_secrets_provider_access](public.project_secrets_provider_access.md) [public.role_mapping_rule_project](public.role_mapping_rule_project.md) [public.shared_credentials](public.shared_credentials.md) [public.shared_workflow](public.shared_workflow.md) [public.variables](public.variables.md) [public.workflow_review_request](public.workflow_review_request.md) | | |
|
||||
| id | varchar(36) | | false | [public.agent_chat_attachments](public.agent_chat_attachments.md) [public.agent_execution_threads](public.agent_execution_threads.md) [public.agents](public.agents.md) [public.data_table](public.data_table.md) [public.folder](public.folder.md) [public.git_connection_project](public.git_connection_project.md) [public.insights_metadata](public.insights_metadata.md) [public.instance_ai_threads](public.instance_ai_threads.md) [public.project_relation](public.project_relation.md) [public.project_secrets_provider_access](public.project_secrets_provider_access.md) [public.role_mapping_rule_project](public.role_mapping_rule_project.md) [public.shared_credentials](public.shared_credentials.md) [public.shared_workflow](public.shared_workflow.md) [public.variables](public.variables.md) [public.workflow_review_request](public.workflow_review_request.md) | | |
|
||||
| name | varchar(255) | | false | | | |
|
||||
| type | varchar(36) | | false | | | |
|
||||
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
@@ -44,6 +44,7 @@ erDiagram
|
||||
"public.agents" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.data_table" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.folder" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.git_connection_project" |o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.insights_metadata" }o--o| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE SET NULL"
|
||||
"public.instance_ai_threads" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
"public.project_relation" }o--|| "public.project" : "FOREIGN KEY (#quot;projectId#quot;) REFERENCES project(id) ON DELETE CASCADE"
|
||||
@@ -146,6 +147,12 @@ erDiagram
|
||||
varchar_36_ projectId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.git_connection_project" {
|
||||
timestamp_3__with_time_zone createdAt
|
||||
varchar_36_ gitConnectionId FK
|
||||
varchar_36_ projectId FK
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.insights_metadata" {
|
||||
integer metaId
|
||||
varchar_36_ projectId FK
|
||||
|
||||
Generated
+9
@@ -64,6 +64,7 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand.
|
||||
| [folder](folder.md) | 6 | | table |
|
||||
| [folder_tag](folder_tag.md) | 2 | | table |
|
||||
| [git_connection](git_connection.md) | 13 | | table |
|
||||
| [git_connection_project](git_connection_project.md) | 4 | | table |
|
||||
| [insights_by_period](insights_by_period.md) | 6 | | table |
|
||||
| [insights_metadata](insights_metadata.md) | 5 | | table |
|
||||
| [insights_raw](insights_raw.md) | 5 | | table |
|
||||
@@ -231,6 +232,8 @@ erDiagram
|
||||
"folder" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"folder_tag" |o--|| "tag_entity" : "FOREIGN KEY (tagId) REFERENCES tag_entity (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"folder_tag" |o--|| "folder" : "FOREIGN KEY (folderId) REFERENCES folder (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"git_connection_project" }o--|| "git_connection" : "FOREIGN KEY (gitConnectionId) REFERENCES git_connection (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"git_connection_project" |o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"insights_by_period" }o--|| "insights_metadata" : "FOREIGN KEY (metaId) REFERENCES insights_metadata (metaId) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"insights_metadata" }o--o| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"insights_metadata" }o--o| "workflow_entity" : "FOREIGN KEY (workflowId) REFERENCES workflow_entity (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
@@ -912,6 +915,12 @@ erDiagram
|
||||
TEXT repositoryUrl
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"git_connection_project" {
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ gitConnectionId FK
|
||||
varchar_36_ projectId PK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"insights_by_period" {
|
||||
INTEGER id
|
||||
INTEGER metaId FK
|
||||
|
||||
+8
-1
@@ -22,7 +22,7 @@ CREATE TABLE "git_connection" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" var
|
||||
| encryptedPassword | TEXT | | true | | | |
|
||||
| encryptedPrivateKey | TEXT | | true | | | |
|
||||
| encryptedUsername | TEXT | | true | | | |
|
||||
| id | varchar(36) | | false | | | |
|
||||
| id | varchar(36) | | false | [git_connection_project](git_connection_project.md) | | |
|
||||
| keyGeneratorType | varchar(16) | | true | | | |
|
||||
| name | varchar(128) | | false | | | |
|
||||
| publicKey | TEXT | | true | | | |
|
||||
@@ -51,6 +51,7 @@ CREATE TABLE "git_connection" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" var
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"git_connection_project" }o--|| "git_connection" : "FOREIGN KEY (gitConnectionId) REFERENCES git_connection (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
||||
"git_connection" {
|
||||
varchar_64_ baseCommit
|
||||
@@ -67,6 +68,12 @@ erDiagram
|
||||
TEXT repositoryUrl
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"git_connection_project" {
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ gitConnectionId FK
|
||||
varchar_36_ projectId PK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# git_connection_project
|
||||
|
||||
## Description
|
||||
|
||||
<details>
|
||||
<summary><strong>Table Definition</strong></summary>
|
||||
|
||||
```sql
|
||||
CREATE TABLE "git_connection_project" ("projectId" varchar(36) PRIMARY KEY NOT NULL, "gitConnectionId" varchar(36) NOT NULL, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), CONSTRAINT "FK_5affe1c6075bd7ac20297b7947c" FOREIGN KEY ("projectId") REFERENCES "project" ("id") ON DELETE CASCADE, CONSTRAINT "FK_12a7fe192a1ee1d933e712e6b1c" FOREIGN KEY ("gitConnectionId") REFERENCES "git_connection" ("id") ON DELETE CASCADE)
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| gitConnectionId | varchar(36) | | false | | [git_connection](git_connection.md) | |
|
||||
| projectId | varchar(36) | | false | | [project](project.md) | |
|
||||
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| - (Foreign key ID: 0) | FOREIGN KEY | FOREIGN KEY (gitConnectionId) REFERENCES git_connection (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| - (Foreign key ID: 1) | FOREIGN KEY | FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE |
|
||||
| projectId | PRIMARY KEY | PRIMARY KEY (projectId) |
|
||||
| sqlite_autoindex_git_connection_project_1 | PRIMARY KEY | PRIMARY KEY (projectId) |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| IDX_12a7fe192a1ee1d933e712e6b1 | CREATE INDEX "IDX_12a7fe192a1ee1d933e712e6b1" ON "git_connection_project" ("gitConnectionId") |
|
||||
| sqlite_autoindex_git_connection_project_1 | PRIMARY KEY (projectId) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
"git_connection_project" }o--|| "git_connection" : "FOREIGN KEY (gitConnectionId) REFERENCES git_connection (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"git_connection_project" |o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
|
||||
"git_connection_project" {
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ gitConnectionId FK
|
||||
varchar_36_ projectId PK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"git_connection" {
|
||||
varchar_64_ baseCommit
|
||||
varchar_255_ branchName
|
||||
varchar_16_ connectionType
|
||||
datetime_3_ createdAt
|
||||
TEXT encryptedPassword
|
||||
TEXT encryptedPrivateKey
|
||||
TEXT encryptedUsername
|
||||
varchar_36_ id PK
|
||||
varchar_16_ keyGeneratorType
|
||||
varchar_128_ name
|
||||
TEXT publicKey
|
||||
TEXT repositoryUrl
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"project" {
|
||||
datetime_3_ createdAt
|
||||
varchar creatorId FK
|
||||
TEXT customTelemetryTags
|
||||
varchar_512_ description
|
||||
TEXT icon
|
||||
varchar_36_ id PK
|
||||
varchar_255_ name
|
||||
varchar_36_ type
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
Generated
+8
-1
@@ -20,7 +20,7 @@ CREATE TABLE "project" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(25
|
||||
| customTelemetryTags | TEXT | '[]' | false | | | |
|
||||
| description | varchar(512) | | true | | | |
|
||||
| icon | TEXT | | true | | | |
|
||||
| id | varchar(36) | | false | [agent_chat_attachments](agent_chat_attachments.md) [agent_execution_threads](agent_execution_threads.md) [agents](agents.md) [data_table](data_table.md) [folder](folder.md) [insights_metadata](insights_metadata.md) [instance_ai_threads](instance_ai_threads.md) [project_relation](project_relation.md) [project_secrets_provider_access](project_secrets_provider_access.md) [role_mapping_rule_project](role_mapping_rule_project.md) [shared_credentials](shared_credentials.md) [shared_workflow](shared_workflow.md) [variables](variables.md) [workflow_review_request](workflow_review_request.md) | | |
|
||||
| id | varchar(36) | | false | [agent_chat_attachments](agent_chat_attachments.md) [agent_execution_threads](agent_execution_threads.md) [agents](agents.md) [data_table](data_table.md) [folder](folder.md) [git_connection_project](git_connection_project.md) [insights_metadata](insights_metadata.md) [instance_ai_threads](instance_ai_threads.md) [project_relation](project_relation.md) [project_secrets_provider_access](project_secrets_provider_access.md) [role_mapping_rule_project](role_mapping_rule_project.md) [shared_credentials](shared_credentials.md) [shared_workflow](shared_workflow.md) [variables](variables.md) [workflow_review_request](workflow_review_request.md) | | |
|
||||
| name | varchar(255) | | false | | | |
|
||||
| type | varchar(36) | | false | | | |
|
||||
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
@@ -50,6 +50,7 @@ erDiagram
|
||||
"agents" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"data_table" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"folder" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"git_connection_project" |o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"insights_metadata" }o--o| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE SET NULL MATCH NONE"
|
||||
"instance_ai_threads" }o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
"project_relation" |o--|| "project" : "FOREIGN KEY (projectId) REFERENCES project (id) ON UPDATE NO ACTION ON DELETE CASCADE MATCH NONE"
|
||||
@@ -152,6 +153,12 @@ erDiagram
|
||||
varchar_36_ projectId FK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"git_connection_project" {
|
||||
datetime_3_ createdAt
|
||||
varchar_36_ gitConnectionId FK
|
||||
varchar_36_ projectId PK
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"insights_metadata" {
|
||||
INTEGER metaId
|
||||
varchar_36_ projectId FK
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import type { MigrationContext, ReversibleMigration } from '../migration-types';
|
||||
|
||||
const TABLE = 'git_connection_project';
|
||||
const PROJECT_TABLE = 'project';
|
||||
const GIT_CONNECTION_TABLE = 'git_connection';
|
||||
|
||||
export class CreateGitConnectionProjectTable1787089039726 implements ReversibleMigration {
|
||||
async up({ schemaBuilder: { createTable, column } }: MigrationContext) {
|
||||
// projectId is the primary key: a project can be linked to at most one
|
||||
// connection, while a connection can hold many project links. Both FKs
|
||||
// cascade so a link disappears when either side is deleted.
|
||||
await createTable(TABLE)
|
||||
.withColumns(
|
||||
column('projectId').varchar(36).primary,
|
||||
column('gitConnectionId').varchar(36).notNull,
|
||||
)
|
||||
.withForeignKey('projectId', {
|
||||
tableName: PROJECT_TABLE,
|
||||
columnName: 'id',
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
.withForeignKey('gitConnectionId', {
|
||||
tableName: GIT_CONNECTION_TABLE,
|
||||
columnName: 'id',
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
// A connection holds many projects; index the FK for connection-scoped
|
||||
// reads ("list projects for this connection") and the cascade delete.
|
||||
.withIndexOn(['gitConnectionId']).withTimestamps;
|
||||
}
|
||||
|
||||
async down({ schemaBuilder: { dropTable } }: MigrationContext) {
|
||||
await dropTable(TABLE);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { WithTimestamps } from '@n8n/db';
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryColumn,
|
||||
type Relation,
|
||||
} from '@n8n/typeorm';
|
||||
|
||||
import { GitConnection } from './git-connection.entity';
|
||||
|
||||
/**
|
||||
* Links a project to a git connection. The `projectId` primary key enforces the
|
||||
* one-connection-per-project rule; a connection can hold many project links.
|
||||
*
|
||||
* The FK to `project` (with cascade delete) is declared in the migration rather
|
||||
* than as a `@ManyToOne`, to keep the module entity decoupled from the core
|
||||
* `Project` entity — mirroring the workflow-reviews pattern.
|
||||
*/
|
||||
@Entity('git_connection_project')
|
||||
export class GitConnectionProject extends WithTimestamps {
|
||||
@PrimaryColumn({ type: 'varchar', length: 36 })
|
||||
projectId: string;
|
||||
|
||||
@Index()
|
||||
@Column({ type: 'varchar', length: 36 })
|
||||
gitConnectionId: string;
|
||||
|
||||
@ManyToOne(() => GitConnection, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'gitConnectionId' })
|
||||
gitConnection: Relation<GitConnection>;
|
||||
}
|
||||
@@ -68,6 +68,11 @@
|
||||
"name": "WorkflowReviewRequest",
|
||||
"path": "packages/@n8n/db/src/entities/workflow-review-request.ee.ts",
|
||||
"reason": "Not transferred with the project. Open reviews are closed when their workflow is archived, moved to another project, or deleted (workflow lifecycle hooks); rows are removed via the projectId FK cascade when the source project itself is deleted, as in ownership-transfer flows. WorkflowReviewActivity and WorkflowReviewActivityComment ride this entity's cascade and have no projectId of their own, so they never appear in this manifest."
|
||||
},
|
||||
{
|
||||
"name": "GitConnectionProject",
|
||||
"path": "packages/cli/src/modules/git-connections.ee/database/entities/git-connection-project.entity.ts",
|
||||
"reason": "Project-scoped git configuration (which connection the project is linked to), not user data. Dropped via the projectId FK cascade when the source project itself is deleted, as in ownership-transfer flows."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
import {
|
||||
createTestMigrationContext,
|
||||
initDbUpToMigration,
|
||||
runSingleMigration,
|
||||
type TestMigrationContext,
|
||||
} from '@n8n/backend-test-utils';
|
||||
import { DbConnection } from '@n8n/db';
|
||||
import { Container } from '@n8n/di';
|
||||
import { DataSource } from '@n8n/typeorm';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
const MIGRATION_NAME = 'CreateGitConnectionProjectTable1787089039726';
|
||||
|
||||
describe('CreateGitConnectionProjectTable migration', () => {
|
||||
let dataSource: DataSource;
|
||||
|
||||
beforeAll(async () => {
|
||||
const dbConnection = Container.get(DbConnection);
|
||||
await dbConnection.init();
|
||||
dataSource = Container.get(DataSource);
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await context.queryRunner.clearDatabase();
|
||||
await context.queryRunner.release();
|
||||
await initDbUpToMigration(MIGRATION_NAME);
|
||||
await runSingleMigration(MIGRATION_NAME);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await Container.get(DbConnection).close();
|
||||
});
|
||||
|
||||
async function insertProject(context: TestMigrationContext, id: string) {
|
||||
const table = context.escape.tableName('project');
|
||||
const now = new Date();
|
||||
await context.runQuery(
|
||||
`INSERT INTO ${table} ("id", "name", "type", "customTelemetryTags", "createdAt", "updatedAt")
|
||||
VALUES (:id, :name, :type, :tags, :createdAt, :updatedAt)`,
|
||||
{ id, name: 'Project', type: 'team', tags: '[]', createdAt: now, updatedAt: now },
|
||||
);
|
||||
}
|
||||
|
||||
async function insertConnection(context: TestMigrationContext, id: string) {
|
||||
const table = context.escape.tableName('git_connection');
|
||||
const now = new Date();
|
||||
await context.runQuery(
|
||||
`INSERT INTO ${table} ("id", "name", "repositoryUrl", "connectionType", "createdAt", "updatedAt")
|
||||
VALUES (:id, :name, :repositoryUrl, :connectionType, :createdAt, :updatedAt)`,
|
||||
{
|
||||
id,
|
||||
name: 'Connection',
|
||||
repositoryUrl: 'https://example.com/org/repo.git',
|
||||
connectionType: 'https',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async function insertLink(
|
||||
context: TestMigrationContext,
|
||||
projectId: string,
|
||||
connectionId: string,
|
||||
) {
|
||||
const table = context.escape.tableName('git_connection_project');
|
||||
const now = new Date();
|
||||
await context.runQuery(
|
||||
`INSERT INTO ${table} ("projectId", "gitConnectionId", "createdAt", "updatedAt")
|
||||
VALUES (:projectId, :gitConnectionId, :createdAt, :updatedAt)`,
|
||||
{ projectId, gitConnectionId: connectionId, createdAt: now, updatedAt: now },
|
||||
);
|
||||
}
|
||||
|
||||
async function countLinks(context: TestMigrationContext, projectId: string) {
|
||||
const table = context.escape.tableName('git_connection_project');
|
||||
const rows = await context.runQuery<Array<{ c: number }>>(
|
||||
`SELECT COUNT(*) as c FROM ${table} WHERE "projectId" = :projectId`,
|
||||
{ projectId },
|
||||
);
|
||||
return Number(rows[0].c);
|
||||
}
|
||||
|
||||
describe('Up migration', () => {
|
||||
it('enforces one connection per project via the primary key', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
const projectId = randomUUID();
|
||||
const connectionId = randomUUID();
|
||||
await insertProject(context, projectId);
|
||||
await insertConnection(context, connectionId);
|
||||
await insertLink(context, projectId, connectionId);
|
||||
|
||||
await expect(insertLink(context, projectId, connectionId)).rejects.toThrow();
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('cascades the link away when the git connection is deleted', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
const projectId = randomUUID();
|
||||
const connectionId = randomUUID();
|
||||
await insertProject(context, projectId);
|
||||
await insertConnection(context, connectionId);
|
||||
await insertLink(context, projectId, connectionId);
|
||||
|
||||
await context.runQuery(
|
||||
`DELETE FROM ${context.escape.tableName('git_connection')} WHERE "id" = :id`,
|
||||
{ id: connectionId },
|
||||
);
|
||||
|
||||
expect(await countLinks(context, projectId)).toBe(0);
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('cascades the link away when the project is deleted', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
const projectId = randomUUID();
|
||||
const connectionId = randomUUID();
|
||||
await insertProject(context, projectId);
|
||||
await insertConnection(context, connectionId);
|
||||
await insertLink(context, projectId, connectionId);
|
||||
|
||||
await context.runQuery(
|
||||
`DELETE FROM ${context.escape.tableName('project')} WHERE "id" = :id`,
|
||||
{ id: projectId },
|
||||
);
|
||||
|
||||
expect(await countLinks(context, projectId)).toBe(0);
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Down migration', () => {
|
||||
it('drops the table and can be re-applied', async () => {
|
||||
await dataSource.undoLastMigration({ transaction: 'each' });
|
||||
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
const table = `${context.tablePrefix}git_connection_project`;
|
||||
expect(await context.queryRunner.hasTable(table)).toBe(false);
|
||||
await context.queryRunner.release();
|
||||
|
||||
await runSingleMigration(MIGRATION_NAME);
|
||||
const context2 = createTestMigrationContext(dataSource);
|
||||
expect(await context2.queryRunner.hasTable(table)).toBe(true);
|
||||
await context2.queryRunner.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user