From 382a2d9ecb95b9f52a36cd5f28fc9ac2b74b76b9 Mon Sep 17 00:00:00 2001 From: Alex Tugarev Date: Fri, 16 Jul 2021 08:31:03 +0000 Subject: [PATCH] [db/server] Link workspaces and projects Co-authored-by: Jan Keromnes --- components/gitpod-db/src/project-db.ts | 3 ++- .../src/typeorm/entity/db-workspace.ts | 6 +++++ .../1626422169862-AddProjectIdToWorkspace.ts | 23 +++++++++++++++++++ .../gitpod-db/src/typeorm/project-db-impl.ts | 21 +++++++++++------ components/gitpod-protocol/src/protocol.ts | 1 + .../ee/src/workspace/workspace-factory.ts | 1 + .../server/src/workspace/workspace-factory.ts | 9 ++++++-- 7 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 components/gitpod-db/src/typeorm/migration/1626422169862-AddProjectIdToWorkspace.ts diff --git a/components/gitpod-db/src/project-db.ts b/components/gitpod-db/src/project-db.ts index a69f702e8432bb..8b18af1283b758 100644 --- a/components/gitpod-db/src/project-db.ts +++ b/components/gitpod-db/src/project-db.ts @@ -8,7 +8,8 @@ import { Project } from "@gitpod/gitpod-protocol"; export const ProjectDB = Symbol('ProjectDB'); export interface ProjectDB { - findProjectsByTeam(teamId: string): Promise; + findProjectByCloneUrl(cloneUrl: string): Promise; findProjectByInstallationId(installationId: string): Promise; + findProjectsByTeam(teamId: string): Promise; createProject(name: string, cloneUrl: string, teamId: string, appInstallationId: string): Promise; } \ No newline at end of file diff --git a/components/gitpod-db/src/typeorm/entity/db-workspace.ts b/components/gitpod-db/src/typeorm/entity/db-workspace.ts index 41276115be0ec4..7997a670b54edd 100644 --- a/components/gitpod-db/src/typeorm/entity/db-workspace.ts +++ b/components/gitpod-db/src/typeorm/entity/db-workspace.ts @@ -27,6 +27,12 @@ export class DBWorkspace implements Workspace { @Column("text") contextURL: string; + @Column({ + default: '', + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + }) + projectId?: string; + @Column() description: string; diff --git a/components/gitpod-db/src/typeorm/migration/1626422169862-AddProjectIdToWorkspace.ts b/components/gitpod-db/src/typeorm/migration/1626422169862-AddProjectIdToWorkspace.ts new file mode 100644 index 00000000000000..3db5b8acf8ed51 --- /dev/null +++ b/components/gitpod-db/src/typeorm/migration/1626422169862-AddProjectIdToWorkspace.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2021 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License-AGPL.txt in the project root for license information. + */ + +import { MigrationInterface, QueryRunner } from "typeorm"; +import { columnExists, tableExists } from "./helper/helper"; + +export class AddProjectIdToWorkspace1626422169862 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + if (await tableExists(queryRunner, "d_b_workspace")) { + if (!(await columnExists(queryRunner, "d_b_workspace", "projectId"))) { + await queryRunner.query("ALTER TABLE d_b_workspace ADD COLUMN `projectId` char(36)"); + } + } + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/components/gitpod-db/src/typeorm/project-db-impl.ts b/components/gitpod-db/src/typeorm/project-db-impl.ts index 8d76a63aa99c00..abe615d3769af1 100644 --- a/components/gitpod-db/src/typeorm/project-db-impl.ts +++ b/components/gitpod-db/src/typeorm/project-db-impl.ts @@ -24,6 +24,16 @@ export class ProjectDBImpl implements ProjectDB { return (await this.getEntityManager()).getRepository(DBProject); } + async findProjectByCloneUrl(cloneUrl: string): Promise { + const repo = await this.getRepo(); + return repo.findOne({ cloneUrl: cloneUrl.toLowerCase() }); + } + + async findProjectByInstallationId(appInstallationId: string): Promise { + const repo = await this.getRepo(); + return repo.findOne({ appInstallationId }); + } + public async findProjectsByTeam(teamId: string): Promise { const repo = await this.getRepo(); return repo.find({ teamId }); @@ -31,22 +41,19 @@ export class ProjectDBImpl implements ProjectDB { public async createProject(name: string, cloneUrl: string, teamId: string, appInstallationId: string): Promise { const repo = await this.getRepo(); + if (repo.findOne({ cloneUrl: cloneUrl.toLowerCase() })) { + throw new Error('A project with the same clone URL already exists'); + } const project: Project = { id: uuidv4(), name, teamId, - cloneUrl, + cloneUrl: cloneUrl.toLowerCase(), appInstallationId, creationTime: new Date().toISOString(), } await repo.save(project); return project; } - - async findProjectByInstallationId(appInstallationId: string): Promise { - const repo = await this.getRepo(); - const project = await repo.findOne({ appInstallationId }); - return project; - } } diff --git a/components/gitpod-protocol/src/protocol.ts b/components/gitpod-protocol/src/protocol.ts index 5b8fb0f86a91b3..3bda3334e2a567 100644 --- a/components/gitpod-protocol/src/protocol.ts +++ b/components/gitpod-protocol/src/protocol.ts @@ -394,6 +394,7 @@ export interface Workspace { contextURL: string; description: string; ownerId: string; + projectId?: string; context: WorkspaceContext; config: WorkspaceConfig; diff --git a/components/server/ee/src/workspace/workspace-factory.ts b/components/server/ee/src/workspace/workspace-factory.ts index ec74dbdfe3b0d1..d74a4acfee7ff2 100644 --- a/components/server/ee/src/workspace/workspace-factory.ts +++ b/components/server/ee/src/workspace/workspace-factory.ts @@ -166,6 +166,7 @@ export class WorkspaceFactoryEE extends WorkspaceFactory { type: "regular", creationTime: new Date().toISOString(), contextURL: normalizedContextURL, + projectId: context.prebuiltWorkspace.projectId, description: this.getDescription(context), ownerId: user.id, context: { diff --git a/components/server/src/workspace/workspace-factory.ts b/components/server/src/workspace/workspace-factory.ts index 4c940d674c3c88..ba7a5aca298296 100644 --- a/components/server/src/workspace/workspace-factory.ts +++ b/components/server/src/workspace/workspace-factory.ts @@ -4,7 +4,7 @@ * See License-AGPL.txt in the project root for license information. */ -import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB } from '@gitpod/gitpod-db/lib'; +import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB, ProjectDB } from '@gitpod/gitpod-db/lib'; import { CommitContext, IssueContext, PullRequestContext, Repository, SnapshotContext, User, Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceProbeContext } from '@gitpod/gitpod-protocol'; import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error'; import { generateWorkspaceID } from '@gitpod/gitpod-protocol/lib/util/generate-workspace-id'; @@ -19,6 +19,7 @@ import { ImageSourceProvider } from './image-source-provider'; export class WorkspaceFactory { @inject(TracedWorkspaceDB) protected readonly db: DBWithTracing; + @inject(ProjectDB) protected readonly projectDB: ProjectDB; @inject(ConfigProvider) protected configProvider: ConfigProvider; @inject(ImageSourceProvider) protected imageSourceProvider: ImageSourceProvider; @@ -135,7 +136,10 @@ export class WorkspaceFactory { const span = TraceContext.startSpan("createForCommit", ctx); try { - const config = await this.configProvider.fetchConfig({span}, user, context); + const [ config, project ] = await Promise.all([ + this.configProvider.fetchConfig({ span }, user, context), + this.projectDB.findProjectByCloneUrl(context.repository.cloneUrl), + ]); const imageSource = await this.imageSourceProvider.getImageSource(ctx, user, context, config); const id = await generateWorkspaceID(); @@ -144,6 +148,7 @@ export class WorkspaceFactory { type: "regular", creationTime: new Date().toISOString(), contextURL: normalizedContextURL, + projectId: project?.id, description: this.getDescription(context), ownerId: user.id, context,