Skip to content

Commit 34bbbf4

Browse files
committed
[db] Implement new Project and Team entities
Also: - set up EditorConfig to trim trailing whitespaces - install Docker VS Code extension by default
1 parent d99cc0e commit 34bbbf4

19 files changed

+322
-13
lines changed

Diff for: .editorconfig

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
[*]
7+
trim_trailing_whitespace = true

Diff for: .gitpod.yml

+6-4
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ tasks:
3838
openMode: split-right
3939
vscode:
4040
extensions:
41-
- hangxingliu.vscode-nginx-conf-hint
42-
- zxh404.vscode-proto3
43-
- ms-kubernetes-tools.vscode-kubernetes-tools
4441
- bajdzis.vscode-database
42+
- EditorConfig.EditorConfig
43+
- golang.go
44+
- hangxingliu.vscode-nginx-conf-hint
4545
- hashicorp.terraform
46+
- ms-azuretools.vscode-docker
47+
- ms-kubernetes-tools.vscode-kubernetes-tools
4648
- stkb.rewrap
47-
- golang.go
49+
- zxh404.vscode-proto3

Diff for: components/gitpod-db/src/container-module.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ import { EduEmailDomainDBImpl } from './typeorm/edu-email-domain-db-impl';
5050
import { EmailDomainFilterDBImpl } from './typeorm/email-domain-filter-db-impl';
5151
import { TeamSubscriptionDBImpl } from './typeorm/team-subscription-db-impl';
5252
import { TypeORMAccountingDBImpl } from './typeorm/accounting-db-impl';
53+
import { TeamDB } from './team-db';
54+
import { TeamDBImpl } from './typeorm/team-db-impl';
55+
import { ProjectDB } from './project-db';
56+
import { ProjectDBImpl } from './typeorm/project-db-impl';
5357

5458
// THE DB container module that contains all DB implementations
5559
export const dbContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => {
@@ -98,7 +102,6 @@ export const dbContainerModule = new ContainerModule((bind, unbind, isBound, reb
98102
};
99103
}).inSingletonScope();
100104

101-
102105
bind(GitpodTableDescriptionProvider).toSelf().inSingletonScope();
103106
bind(TableDescriptionProvider).toService(GitpodTableDescriptionProvider);
104107
bind(PeriodicDbDeleter).toSelf().inSingletonScope();
@@ -109,6 +112,11 @@ export const dbContainerModule = new ContainerModule((bind, unbind, isBound, reb
109112

110113
bind(AuthCodeRepositoryDB).toSelf().inSingletonScope();
111114

115+
bind(TeamDBImpl).toSelf().inSingletonScope();
116+
bind(TeamDB).toService(TeamDBImpl);
117+
bind(ProjectDBImpl).toSelf().inSingletonScope();
118+
bind(ProjectDB).toService(ProjectDBImpl);
119+
112120
// com concerns
113121
bind(AccountingDB).to(TypeORMAccountingDBImpl).inSingletonScope();
114122
bind(TeamSubscriptionDB).to(TeamSubscriptionDBImpl).inSingletonScope();

Diff for: components/gitpod-db/src/index.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@ export * from "./accounting-db";
3434
export * from "./team-subscription-db";
3535
export * from "./edu-email-domain-db";
3636
export * from "./email-domain-filter-db";
37-
export * from "./typeorm/entity/db-account-entry";
37+
export * from "./typeorm/entity/db-account-entry";
38+
export * from "./project-db";
39+
export * from "./team-db";

Diff for: components/gitpod-db/src/project-db.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the Gitpod Enterprise Source Code License,
4+
* See License.enterprise.txt in the project root folder.
5+
*/
6+
7+
export const ProjectDB = Symbol('ProjectDB');
8+
export interface ProjectDB {
9+
}

Diff for: components/gitpod-db/src/tables.ts

+18
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,24 @@ export class GitpodTableDescriptionProvider implements TableDescriptionProvider
214214
primaryKeys: ['userId'],
215215
timeColumn: '_lastModified',
216216
},
217+
{
218+
name: 'd_b_team',
219+
primaryKeys: ['id'],
220+
deletionColumn: 'deleted',
221+
timeColumn: '_lastModified',
222+
},
223+
{
224+
name: 'd_b_team_membership',
225+
primaryKeys: ['id'],
226+
deletionColumn: 'deleted',
227+
timeColumn: '_lastModified',
228+
},
229+
{
230+
name: 'd_b_project',
231+
primaryKeys: ['id'],
232+
deletionColumn: 'deleted',
233+
timeColumn: '_lastModified',
234+
},
217235
]
218236

219237
public getSortedTables(): TableDescription[] {

Diff for: components/gitpod-db/src/team-db.spec.db.ts

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the Gitpod Enterprise Source Code License,
4+
* See License.enterprise.txt in the project root folder.
5+
*/
6+
7+
import * as chai from 'chai';
8+
const expect = chai.expect;
9+
import { suite, test, timeout } from 'mocha-typescript';
10+
11+
import { testContainer } from './test-container';
12+
import { TeamDBImpl } from './typeorm/team-db-impl';
13+
import { TypeORMUserDBImpl } from './typeorm/user-db-impl';
14+
import { TypeORM } from './typeorm/typeorm';
15+
import { DBTeam } from './typeorm/entity/db-team';
16+
import { DBTeamMembership } from './typeorm/entity/db-team-membership';
17+
import { DBUser } from './typeorm/entity/db-user';
18+
19+
@suite class TeamDBSpec {
20+
21+
db = testContainer.get<TeamDBImpl>(TeamDBImpl);
22+
userDb = testContainer.get<TypeORMUserDBImpl>(TypeORMUserDBImpl);
23+
24+
async before() {
25+
await this.wipeRepo();
26+
}
27+
28+
async after() {
29+
await this.wipeRepo();
30+
}
31+
32+
async wipeRepo() {
33+
const typeorm = testContainer.get<TypeORM>(TypeORM);
34+
const manager = await typeorm.getConnection();
35+
await manager.getRepository(DBTeam).delete({});
36+
await manager.getRepository(DBTeamMembership).delete({});
37+
await manager.getRepository(DBUser).delete({});
38+
}
39+
40+
@test(timeout(10000))
41+
public async findTeamsByUser() {
42+
const user = await this.userDb.newUser();
43+
const dbResult = await this.db.findTeamsByUser(user.id);
44+
expect(dbResult.length).to.eq(0);
45+
}
46+
47+
}
48+
module.exports = new TeamDBSpec()

Diff for: components/gitpod-db/src/team-db.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the Gitpod Enterprise Source Code License,
4+
* See License.enterprise.txt in the project root folder.
5+
*/
6+
7+
import { Team } from "@gitpod/gitpod-protocol";
8+
9+
export const TeamDB = Symbol('TeamDB');
10+
export interface TeamDB {
11+
findTeamsByUser(userId: string): Promise<Team[]>;
12+
}

Diff for: components/gitpod-db/src/typeorm/deleted-entry-gc.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@ const tables: TableWithDeletion[] = [
4040
{ deletionColumn: "deleted", name: "d_b_workspace" },
4141
{ deletionColumn: "deleted", name: "d_b_workspace_instance" },
4242
{ deletionColumn: "deleted", name: "d_b_token_entry" },
43-
{ deletionColumn: "deleted", name: "d_b_gitpod_token" }
43+
{ deletionColumn: "deleted", name: "d_b_gitpod_token" },
44+
{ deletionColumn: "deleted", name: "d_b_one_time_secret" },
45+
{ deletionColumn: "deleted", name: "d_b_auth_provider_entry" },
46+
{ deletionColumn: "deleted", name: "d_b_code_sync_resource" },
47+
{ deletionColumn: "deleted", name: "d_b_team" },
48+
{ deletionColumn: "deleted", name: "d_b_team_membership" },
49+
{ deletionColumn: "deleted", name: "d_b_project" }
4450
];
4551

4652
interface TableWithDeletion {
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { Entity, Column, PrimaryColumn, Index } from "typeorm";
8+
import { TypeORM } from "../typeorm";
9+
10+
@Entity()
11+
// on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync
12+
export class DBProject {
13+
@PrimaryColumn(TypeORM.UUID_COLUMN_TYPE)
14+
id: string;
15+
16+
@Column()
17+
cloneUrl: string;
18+
19+
@Column(TypeORM.UUID_COLUMN_TYPE)
20+
@Index("ind_teamId")
21+
teamId: string;
22+
23+
@Column()
24+
appInstallationId: string;
25+
26+
@Column("varchar")
27+
creationTime: string;
28+
29+
// This column triggers the db-sync deletion mechanism. It's not intended for public consumption.
30+
@Column()
31+
deleted: boolean;
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { Entity, Column, PrimaryColumn, Index } from "typeorm";
8+
import { TypeORM } from "../typeorm";
9+
10+
@Entity()
11+
// on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync
12+
export class DBTeamMembership {
13+
@PrimaryColumn(TypeORM.UUID_COLUMN_TYPE)
14+
id: string;
15+
16+
@Column(TypeORM.UUID_COLUMN_TYPE)
17+
@Index("ind_teamId")
18+
teamId: string;
19+
20+
@Column(TypeORM.UUID_COLUMN_TYPE)
21+
@Index("ind_userId")
22+
userId: string;
23+
24+
// This column triggers the db-sync deletion mechanism. It's not intended for public consumption.
25+
@Column()
26+
deleted: boolean;
27+
}

Diff for: components/gitpod-db/src/typeorm/entity/db-team.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { Entity, Column, PrimaryColumn } from "typeorm";
8+
import { TypeORM } from "../typeorm";
9+
10+
@Entity()
11+
// on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync
12+
export class DBTeam {
13+
@PrimaryColumn(TypeORM.UUID_COLUMN_TYPE)
14+
id: string;
15+
16+
@Column("varchar")
17+
name: string;
18+
19+
@Column("varchar")
20+
creationTime: string;
21+
22+
// This column triggers the db-sync deletion mechanism. It's not intended for public consumption.
23+
@Column()
24+
deleted: boolean;
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { MigrationInterface, QueryRunner } from "typeorm";
8+
9+
export class TeamsAndProjects1622468446118 implements MigrationInterface {
10+
11+
public async up(queryRunner: QueryRunner): Promise<any> {
12+
await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_team` (`id` char(36) NOT NULL, `name` varchar(255) NOT NULL, `creationTime` varchar(255) NOT NULL, `deleted` tinyint(4) NOT NULL DEFAULT '0', `_lastModified` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
13+
await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_team_membership` (`id` char(36) NOT NULL, `teamId` char(36) NOT NULL, `userId` char(36) NOT NULL, `deleted` tinyint(4) NOT NULL DEFAULT '0', `_lastModified` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`), KEY `ind_teamId` (`teamId`), KEY `ind_userId` (`userId`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
14+
await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_project` (`id` char(36) NOT NULL, `cloneUrl` varchar(255) NOT NULL, `teamId` char(36) NOT NULL, `appInstallationId` varchar(255) NOT NULL, `creationTime` varchar(255) NOT NULL, `deleted` tinyint(4) NOT NULL DEFAULT '0', `_lastModified` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (`id`), KEY `ind_teamId` (`teamId`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
15+
}
16+
17+
public async down(queryRunner: QueryRunner): Promise<any> {
18+
// this is a one-way idempotent 'migration', no rollback possible for a nonempty DB
19+
}
20+
21+
}

Diff for: components/gitpod-db/src/typeorm/project-db-impl.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { inject, injectable } from "inversify";
8+
import { TypeORM } from "./typeorm";
9+
import { Repository } from "typeorm";
10+
import { ProjectDB } from "../project-db";
11+
import { DBProject } from "./entity/db-project";
12+
13+
@injectable()
14+
export class ProjectDBImpl implements ProjectDB {
15+
@inject(TypeORM) typeORM: TypeORM;
16+
17+
protected async getEntityManager() {
18+
return (await this.typeORM.getConnection()).manager;
19+
}
20+
21+
async getRepo(): Promise<Repository<DBProject>> {
22+
return (await this.getEntityManager()).getRepository<DBProject>(DBProject);
23+
}
24+
}

Diff for: components/gitpod-db/src/typeorm/team-db-impl.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Copyright (c) 2021 Gitpod GmbH. All rights reserved.
3+
* Licensed under the GNU Affero General Public License (AGPL).
4+
* See License-AGPL.txt in the project root for license information.
5+
*/
6+
7+
import { inject, injectable } from "inversify";
8+
import { TypeORM } from "./typeorm";
9+
import { Repository } from "typeorm";
10+
import { TeamDB } from "../team-db";
11+
import { DBTeam } from "./entity/db-team";
12+
import { DBTeamMembership } from "./entity/db-team-membership";
13+
import { Team } from "@gitpod/gitpod-protocol";
14+
15+
@injectable()
16+
export class TeamDBImpl implements TeamDB {
17+
@inject(TypeORM) typeORM: TypeORM;
18+
19+
protected async getEntityManager() {
20+
return (await this.typeORM.getConnection()).manager;
21+
}
22+
23+
protected async getTeamRepo(): Promise<Repository<DBTeam>> {
24+
return (await this.getEntityManager()).getRepository<DBTeam>(DBTeam);
25+
}
26+
27+
protected async getMembershipRepo(): Promise<Repository<DBTeamMembership>> {
28+
return (await this.getEntityManager()).getRepository<DBTeamMembership>(DBTeamMembership);
29+
}
30+
31+
public async findTeamsByUser(userId: string): Promise<Team[]> {
32+
const teamRepo = await this.getTeamRepo();
33+
const membershipRepo = await this.getMembershipRepo();
34+
const memberships = await membershipRepo.find({ userId });
35+
return teamRepo.findByIds(memberships.map(m => m.teamId));
36+
}
37+
}

Diff for: components/gitpod-protocol/src/gitpod-service.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
WhitelistedRepository, WorkspaceImageBuild, AuthProviderInfo, Branding, CreateWorkspaceMode,
1010
Token, UserEnvVarValue, ResolvePluginsParams, PreparePluginUploadParams, Terms,
1111
ResolvedPlugins, Configuration, InstallPluginsParams, UninstallPluginParams, UserInfo, GitpodTokenType,
12-
GitpodToken, AuthProviderEntry, GuessGitTokenScopesParams, GuessedGitTokenScopes
12+
GitpodToken, AuthProviderEntry, GuessGitTokenScopesParams, GuessedGitTokenScopes, Team
1313
} from './protocol';
1414
import { JsonRpcProxy, JsonRpcServer } from './messaging/proxy-factory';
1515
import { Disposable, CancellationTokenSource } from 'vscode-jsonrpc';
@@ -107,6 +107,9 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
107107
setEnvVar(variable: UserEnvVarValue): Promise<void>;
108108
deleteEnvVar(variable: UserEnvVarValue): Promise<void>;
109109

110+
// teams and projects
111+
getTeams(): Promise<Team[]>;
112+
110113
// content service
111114
getContentBlobUploadUrl(name: string): Promise<string>
112115
getContentBlobDownloadUrl(name: string): Promise<string>

Diff for: components/gitpod-protocol/src/protocol.ts

+18
Original file line numberDiff line numberDiff line change
@@ -1177,3 +1177,21 @@ export interface Terms {
11771177
readonly content: string;
11781178
readonly formElements?: object;
11791179
}
1180+
1181+
export interface Project {
1182+
id: string;
1183+
cloneUrl: string;
1184+
teamId: string;
1185+
appInstallationId: string;
1186+
creationTime: string;
1187+
/** This is a flag that triggers the HARD DELETION of this entity */
1188+
deleted?: boolean;
1189+
}
1190+
1191+
export interface Team {
1192+
id: string;
1193+
name: string;
1194+
creationTime: string;
1195+
/** This is a flag that triggers the HARD DELETION of this entity */
1196+
deleted?: boolean;
1197+
}

0 commit comments

Comments
 (0)