diff --git a/components/gitpod-db/src/accounting-db.spec.db.ts b/components/gitpod-db/src/accounting-db.spec.db.ts index 6dea7f07d2d3ea..0002a93b93b35a 100644 --- a/components/gitpod-db/src/accounting-db.spec.db.ts +++ b/components/gitpod-db/src/accounting-db.spec.db.ts @@ -4,24 +4,24 @@ * See License.enterprise.txt in the project root folder. */ -import { Subscription } from '@gitpod/gitpod-protocol/lib/accounting-protocol'; -import * as chai from 'chai'; -import { suite, test, timeout } from 'mocha-typescript'; -import { QueryRunner } from 'typeorm'; -import { AccountingDB } from './accounting-db'; -import { oneMonthLater, rightAfter, rightBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; -import { DBAccountEntry } from './typeorm/entity/db-account-entry'; -import { TransactionalAccountingDBImpl } from './typeorm/accounting-db-impl'; -import { DBWorkspace } from './typeorm/entity/db-workspace'; -import { DBWorkspaceInstance } from './typeorm/entity/db-workspace-instance'; -import { DBSubscription } from './typeorm/entity/db-subscription'; -import { testContainer } from './test-container'; -import { TypeORM } from './typeorm/typeorm'; +import { Subscription } from "@gitpod/gitpod-protocol/lib/accounting-protocol"; +import * as chai from "chai"; +import { suite, test, timeout } from "mocha-typescript"; +import { QueryRunner } from "typeorm"; +import { AccountingDB } from "./accounting-db"; +import { oneMonthLater, rightAfter, rightBefore } from "@gitpod/gitpod-protocol/lib/util/timeutil"; +import { DBAccountEntry } from "./typeorm/entity/db-account-entry"; +import { TransactionalAccountingDBImpl } from "./typeorm/accounting-db-impl"; +import { DBWorkspace } from "./typeorm/entity/db-workspace"; +import { DBWorkspaceInstance } from "./typeorm/entity/db-workspace-instance"; +import { DBSubscription } from "./typeorm/entity/db-subscription"; +import { testContainer } from "./test-container"; +import { TypeORM } from "./typeorm/typeorm"; const expect = chai.expect; -@suite @timeout(5000) +@suite +@timeout(5000) export class AccountingDBSpec { - typeORM = testContainer.get(TypeORM); db: AccountingDB; queryRunner: QueryRunner; @@ -37,7 +37,7 @@ export class AccountingDBSpec { this.queryRunner = connection.createQueryRunner(); await this.queryRunner.connect(); await this.queryRunner.startTransaction(); - this.db = new TransactionalAccountingDBImpl(this.queryRunner.manager) + this.db = new TransactionalAccountingDBImpl(this.queryRunner.manager); } async after() { @@ -54,7 +54,7 @@ export class AccountingDBSpec { startDate: now, endDate: later, amount: 1.01, - planId: 'test' + planId: "test", }; await this.db.newSubscription(subscription); @@ -80,7 +80,7 @@ export class AccountingDBSpec { startDate: now, endDate: undefined, // open ended amount: 1.01, - planId: 'test' + planId: "test", }; await this.db.newSubscription(subscription); @@ -104,43 +104,47 @@ export class AccountingDBSpec { startDate: now, endDate: undefined, // open ended amount: 1.01, - planId: 'test' + planId: "test", }; const dbSubscription = await this.db.newSubscription(subscription); expectExactlyOne(await this.db.findActiveSubscriptions(now, rightAfter(later)), subscription); expect(await this.db.findActiveSubscriptions(rightBefore(now), rightBefore(now))).to.be.empty; Subscription.cancelSubscription(dbSubscription, later); - await this.db.storeSubscription(dbSubscription) + await this.db.storeSubscription(dbSubscription); expect(await this.db.findActiveSubscriptions(rightAfter(later), rightAfter(later))).to.be.empty; - await this.db.storeSubscription(dbSubscription) + await this.db.storeSubscription(dbSubscription); expect(await this.db.findActiveSubscriptions(rightAfter(later), rightAfter(later))).to.be.empty; } @test public async subscriptionsForUser() { const now = new Date().toISOString(); - const later = oneMonthLater(now, 31) + const later = oneMonthLater(now, 31); const subscription = { userId: "Open ended", startDate: now, endDate: undefined, // open ended amount: 1.01, - planId: 'test' + planId: "test", }; let dbSubscription = await this.db.newSubscription(subscription); expectExactlyOne(await this.db.findActiveSubscriptionsForUser(subscription.userId, now), subscription); - expect(await this.db.findActiveSubscriptionsForUser(subscription.userId, rightBefore(now))).to.be.an('array').and.empty; + expect(await this.db.findActiveSubscriptionsForUser(subscription.userId, rightBefore(now))).to.be.an("array") + .and.empty; expectExactlyOne(await this.db.findActiveSubscriptionsForUser(subscription.userId, later), subscription); Subscription.cancelSubscription(dbSubscription, later); await this.db.storeSubscription(dbSubscription); - expectExactlyOne(await this.db.findActiveSubscriptionsForUser(subscription.userId, rightBefore(later)), dbSubscription); - expect(await this.db.findActiveSubscriptionsForUser(subscription.userId, later)).to.be.an('array').and.empty; + expectExactlyOne( + await this.db.findActiveSubscriptionsForUser(subscription.userId, rightBefore(later)), + dbSubscription, + ); + expect(await this.db.findActiveSubscriptionsForUser(subscription.userId, later)).to.be.an("array").and.empty; } } const expectExactlyOne = (result: T[], expectation: T) => { expect(result.length).to.be.equal(1); expect(result[0]).to.deep.include(expectation); -} +}; -module.exports = new AccountingDBSpec +module.exports = new AccountingDBSpec(); diff --git a/components/gitpod-db/src/accounting-db.ts b/components/gitpod-db/src/accounting-db.ts index 1befacedcaf844..65deebf58b2f7d 100644 --- a/components/gitpod-db/src/accounting-db.ts +++ b/components/gitpod-db/src/accounting-db.ts @@ -4,30 +4,38 @@ * See License.enterprise.txt in the project root folder. */ -import { AccountEntry, Subscription, SubscriptionAndUser, Credit } from "@gitpod/gitpod-protocol/lib/accounting-protocol"; +import { + AccountEntry, + Subscription, + SubscriptionAndUser, + Credit, +} from "@gitpod/gitpod-protocol/lib/accounting-protocol"; import { DBSubscriptionAdditionalData } from "./typeorm/entity/db-subscription"; import { EntityManager } from "typeorm"; -export const TransactionalAccountingDBFactory = Symbol('TransactionalAccountingDBFactory'); +export const TransactionalAccountingDBFactory = Symbol("TransactionalAccountingDBFactory"); export interface TransactionalAccountingDBFactory { (manager: EntityManager): AccountingDB; } -export const AccountingDB = Symbol('AccountingDB'); +export const AccountingDB = Symbol("AccountingDB"); export interface AccountingDB { - newAccountEntry(entry: Omit): Promise; + newAccountEntry(entry: Omit): Promise; storeAccountEntry(AccountEntry: AccountEntry): void; findAccountEntriesFor(userId: string, fromDate: string, toDate: string): Promise; findOpenCredits(userId: string, date: string): Promise; - newSubscription(subscription: Omit): Promise; + newSubscription(subscription: Omit): Promise; storeSubscription(subscription: Subscription): Promise; findSubscriptionById(id: string): Promise; - deleteSubscription(subscription: Subscription): Promise + deleteSubscription(subscription: Subscription): Promise; findActiveSubscriptions(fromDate: string, toDate: string): Promise; findActiveSubscriptionsForUser(userId: string, fromDate: string): Promise; - findActiveSubscriptionsByIdentity(authId: string[], authProvider: string): Promise<{ [authId:string]:SubscriptionAndUser[] }>; + findActiveSubscriptionsByIdentity( + authId: string[], + authProvider: string, + ): Promise<{ [authId: string]: SubscriptionAndUser[] }>; findActiveSubscriptionByPlanID(planID: string, date: string): Promise; findAllSubscriptionsForUser(userId: string): Promise; findSubscriptionsForUserInPeriod(userId: string, fromDate: string, toDate: string): Promise; @@ -38,7 +46,12 @@ export interface AccountingDB { hadSubscriptionCreatedWithCoupon(userId: string, coupon: string): Promise; findSubscriptionAdditionalData(paymentReference: string): Promise; - transaction(closure: (db: AccountingDB)=>Promise, closures?: ((manager: EntityManager) => Promise)[]): Promise; + transaction( + closure: (db: AccountingDB) => Promise, + closures?: ((manager: EntityManager) => Promise)[], + ): Promise; - storeSubscriptionAdditionalData(subscriptionData: DBSubscriptionAdditionalData): Promise; + storeSubscriptionAdditionalData( + subscriptionData: DBSubscriptionAdditionalData, + ): Promise; } diff --git a/components/gitpod-db/src/app-installation-db.ts b/components/gitpod-db/src/app-installation-db.ts index abe86451f302f0..35a516ea102d9c 100644 --- a/components/gitpod-db/src/app-installation-db.ts +++ b/components/gitpod-db/src/app-installation-db.ts @@ -6,13 +6,21 @@ import { AppInstallation, AppInstallationPlatform } from "@gitpod/gitpod-protocol"; -export const AppInstallationDB = Symbol('AppInstallationDB'); +export const AppInstallationDB = Symbol("AppInstallationDB"); export interface AppInstallationDB { - - recordNewInstallation(platform: AppInstallationPlatform, source: 'user' | 'platform', installationID: string, ownerUserID?: string, platformUserID?: string): Promise; - recordUninstallation(platform: AppInstallationPlatform, source: 'user' | 'platform', installationID: string): Promise; + recordNewInstallation( + platform: AppInstallationPlatform, + source: "user" | "platform", + installationID: string, + ownerUserID?: string, + platformUserID?: string, + ): Promise; + recordUninstallation( + platform: AppInstallationPlatform, + source: "user" | "platform", + installationID: string, + ): Promise; findInstallation(platform: AppInstallationPlatform, installationID: string): Promise; - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/auth-provider-entry-db.ts b/components/gitpod-db/src/auth-provider-entry-db.ts index bb8b037037b30a..486ad2198f665c 100644 --- a/components/gitpod-db/src/auth-provider-entry-db.ts +++ b/components/gitpod-db/src/auth-provider-entry-db.ts @@ -7,7 +7,7 @@ import { AuthProviderEntry as AuthProviderEntry } from "@gitpod/gitpod-protocol"; import { createHash } from "crypto"; -export const AuthProviderEntryDB = Symbol('AuthProviderEntryDB'); +export const AuthProviderEntryDB = Symbol("AuthProviderEntryDB"); export interface AuthProviderEntryDB { storeAuthProvider(ap: AuthProviderEntry, updateOAuthRevision: boolean): Promise; @@ -21,5 +21,5 @@ export interface AuthProviderEntryDB { } export function hashOAuth(oauth: AuthProviderEntry["oauth"]): string { - return createHash('sha256').update(JSON.stringify(oauth)).digest('hex'); -} \ No newline at end of file + return createHash("sha256").update(JSON.stringify(oauth)).digest("hex"); +} diff --git a/components/gitpod-db/src/auth-provider-entry.spec.db.ts b/components/gitpod-db/src/auth-provider-entry.spec.db.ts index 3cef8be3702093..073995e218d166 100644 --- a/components/gitpod-db/src/auth-provider-entry.spec.db.ts +++ b/components/gitpod-db/src/auth-provider-entry.spec.db.ts @@ -4,18 +4,18 @@ * See License.enterprise.txt in the project root folder. */ -import * as chai from 'chai'; -import { suite, test, timeout } from 'mocha-typescript'; -import { testContainer } from './test-container'; -import { TypeORM } from './typeorm/typeorm'; -import { AuthProviderEntryDB } from '.'; -import { DBAuthProviderEntry } from './typeorm/entity/db-auth-provider-entry'; -import { DeepPartial } from '@gitpod/gitpod-protocol/lib/util/deep-partial'; +import * as chai from "chai"; +import { suite, test, timeout } from "mocha-typescript"; +import { testContainer } from "./test-container"; +import { TypeORM } from "./typeorm/typeorm"; +import { AuthProviderEntryDB } from "."; +import { DBAuthProviderEntry } from "./typeorm/entity/db-auth-provider-entry"; +import { DeepPartial } from "@gitpod/gitpod-protocol/lib/util/deep-partial"; const expect = chai.expect; -@suite @timeout(5000) +@suite +@timeout(5000) export class AuthProviderEntryDBSpec { - typeORM = testContainer.get(TypeORM); db = testContainer.get(AuthProviderEntryDB); @@ -40,7 +40,7 @@ export class AuthProviderEntryDBSpec { id: "0049b9d2-005f-43c2-a0ae-76377805d8b8", host, ownerId, - status: 'verified', + status: "verified", type: "GitHub", oauthRevision: undefined, deleted: false, @@ -87,8 +87,10 @@ export class AuthProviderEntryDBSpec { const loadedAp = await this.db.findByHost(ap.host); expect(loadedAp, "findByHost()").to.deep.equal(ap); - expect(loadedAp?.oauthRevision, "findByHost()").to.equal("e05ea6fab8efcaba4b3246c2b2d3931af897c3bc2c1cf075c31614f0954f9840"); + expect(loadedAp?.oauthRevision, "findByHost()").to.equal( + "e05ea6fab8efcaba4b3246c2b2d3931af897c3bc2c1cf075c31614f0954f9840", + ); } } -module.exports = AuthProviderEntryDBSpec +module.exports = AuthProviderEntryDBSpec; diff --git a/components/gitpod-db/src/config.ts b/components/gitpod-db/src/config.ts index 1ff6cbac1ec2f2..92a7ea8cb7b1fa 100644 --- a/components/gitpod-db/src/config.ts +++ b/components/gitpod-db/src/config.ts @@ -4,21 +4,21 @@ * See License-AGPL.txt in the project root for license information. */ -import { injectable } from 'inversify'; -import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; -import { getEnvVarParsed, getEnvVar } from '@gitpod/gitpod-protocol/lib/env'; -import { ConnectionConfig } from 'mysql'; +import { injectable } from "inversify"; +import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; +import { getEnvVarParsed, getEnvVar } from "@gitpod/gitpod-protocol/lib/env"; +import { ConnectionConfig } from "mysql"; @injectable() export class Config { get dbConfig(): DatabaseConfig { // defaults to be used only in tests const dbSetup = { - host: process.env.DB_HOST || 'localhost', - port: getEnvVarParsed('DB_PORT', Number.parseInt, '3306'), - username: process.env.DB_USERNAME || 'gitpod', - password: process.env.DB_PASSWORD || 'test', - database: process.env.DB_NAME || 'gitpod' + host: process.env.DB_HOST || "localhost", + port: getEnvVarParsed("DB_PORT", Number.parseInt, "3306"), + username: process.env.DB_USERNAME || "gitpod", + password: process.env.DB_PASSWORD || "test", + database: process.env.DB_NAME || "gitpod", }; log.info(`Using DB: ${dbSetup.host}:${dbSetup.port}/${dbSetup.database}`); @@ -33,17 +33,17 @@ export class Config { port: dbConfig.port, user: dbConfig.username, password: dbConfig.password, - database: dbConfig.database + database: dbConfig.database, }; } get dbEncryptionKeys(): string { - return getEnvVar('DB_ENCRYPTION_KEYS'); + return getEnvVar("DB_ENCRYPTION_KEYS"); } get deletedEntryGCConfig(): DeletedEntryGCConfig { - const enabled = getEnvVar('DB_DELETED_ENTRIES_GC_ENABLED', 'true') === 'true'; - const intervalMS = parseInt(getEnvVar('DB_DELETED_ENTRIES_GC_INTERVAL', (10 * 60 * 1000).toString())); + const enabled = getEnvVar("DB_DELETED_ENTRIES_GC_ENABLED", "true") === "true"; + const intervalMS = parseInt(getEnvVar("DB_DELETED_ENTRIES_GC_INTERVAL", (10 * 60 * 1000).toString())); return { enabled, intervalMS }; } } @@ -59,4 +59,4 @@ export interface DatabaseConfig { export interface DeletedEntryGCConfig { enabled: boolean; intervalMS: number; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/container-module.ts b/components/gitpod-db/src/container-module.ts index 6b66f6a7a29e03..f8af836c841884 100644 --- a/components/gitpod-db/src/container-module.ts +++ b/components/gitpod-db/src/container-module.ts @@ -4,61 +4,64 @@ * See License-AGPL.txt in the project root for license information. */ -import { ContainerModule } from 'inversify'; - -import { WorkspaceDB } from './workspace-db'; -import { TypeORMWorkspaceDBImpl, TransactionalWorkspaceDbImpl } from './typeorm/workspace-db-impl'; -import { TypeORMUserDBImpl } from './typeorm/user-db-impl'; -import { UserDB } from './user-db'; -import { Config } from './config'; -import { UserMessageViewsDB } from './user-message-views-db'; -import { TypeORMUserMessageViewsDBImpl } from './typeorm/user-message-views-db-impl'; -import { UserStorageResourcesDB } from './user-storage-resources-db'; -import { TypeORMUserStorageResourcesDBImpl } from './typeorm/user-storage-resources-db-impl'; -import { TypeORM } from './typeorm/typeorm'; -import { encryptionModule } from '@gitpod/gitpod-protocol/lib/encryption/container-module'; -import { KeyProviderImpl, KeyProviderConfig } from '@gitpod/gitpod-protocol/lib/encryption/key-provider'; -import { DBWithTracing, bindDbWithTracing, TracedWorkspaceDB, TracedUserDB, TracedOneTimeSecretDB } from './traced-db'; -import { OneTimeSecretDB } from './one-time-secret-db'; -import { DeletedEntryGC } from './typeorm/deleted-entry-gc'; -import { TypeORMAppInstallationDBImpl } from './typeorm/app-installation-db-impl'; -import { AppInstallationDB } from './app-installation-db'; -import { TheiaPluginDBImpl } from './typeorm/theia-plugin-db-impl'; -import { TheiaPluginDB } from './theia-plugin-db'; -import { TypeORMOneTimeSecretDBImpl } from './typeorm/one-time-secret-db-impl'; -import { PendingGithubEventDB, TransactionalPendingGithubEventDBFactory } from './pending-github-event-db'; -import { TransactionalPendingGithubEventDBImpl, TypeORMPendingGithubEventDBImpl } from './typeorm/pending-github-event-db-impl'; -import { GitpodTableDescriptionProvider, TableDescriptionProvider } from './tables'; -import { PeriodicDbDeleter } from './periodic-deleter'; -import { TermsAcceptanceDB } from './terms-acceptance-db'; -import { TermsAcceptanceDBImpl } from './typeorm/terms-acceptance-db-impl'; -import { CodeSyncResourceDB } from './typeorm/code-sync-resource-db'; -import { WorkspaceClusterDBImpl } from './typeorm/workspace-cluster-db-impl'; -import { WorkspaceClusterDB } from './workspace-cluster-db'; -import { AuthCodeRepositoryDB } from './typeorm/auth-code-repository-db'; -import { AuthProviderEntryDB } from './auth-provider-entry-db'; -import { AuthProviderEntryDBImpl } from './typeorm/auth-provider-entry-db-impl'; -import { TeamSubscriptionDB } from './team-subscription-db'; -import { AccountingDB, TransactionalAccountingDBFactory } from './accounting-db'; -import { EmailDomainFilterDB } from './email-domain-filter-db'; -import { EduEmailDomainDB } from './edu-email-domain-db'; -import { EMailDB } from './email-db'; -import { LicenseDB } from './license-db'; -import { LicenseDBImpl } from './typeorm/license-db-impl'; -import { TypeORMEMailDBImpl } from './typeorm/email-db-impl'; -import { EduEmailDomainDBImpl } from './typeorm/edu-email-domain-db-impl'; -import { EmailDomainFilterDBImpl } from './typeorm/email-domain-filter-db-impl'; -import { TeamSubscriptionDBImpl } from './typeorm/team-subscription-db-impl'; -import { TransactionalAccountingDBImpl, TypeORMAccountingDBImpl } from './typeorm/accounting-db-impl'; -import { TeamDB } from './team-db'; -import { TeamDBImpl } from './typeorm/team-db-impl'; -import { ProjectDB } from './project-db'; -import { ProjectDBImpl } from './typeorm/project-db-impl'; -import { EntityManager } from 'typeorm'; -import { OssAllowListDB } from './oss-allowlist-db'; -import { OssAllowListDBImpl } from './typeorm/oss-allowlist-db-impl'; -import { TypeORMInstallationAdminImpl } from './typeorm/installation-admin-db-impl'; -import { InstallationAdminDB } from './installation-admin-db'; +import { ContainerModule } from "inversify"; + +import { WorkspaceDB } from "./workspace-db"; +import { TypeORMWorkspaceDBImpl, TransactionalWorkspaceDbImpl } from "./typeorm/workspace-db-impl"; +import { TypeORMUserDBImpl } from "./typeorm/user-db-impl"; +import { UserDB } from "./user-db"; +import { Config } from "./config"; +import { UserMessageViewsDB } from "./user-message-views-db"; +import { TypeORMUserMessageViewsDBImpl } from "./typeorm/user-message-views-db-impl"; +import { UserStorageResourcesDB } from "./user-storage-resources-db"; +import { TypeORMUserStorageResourcesDBImpl } from "./typeorm/user-storage-resources-db-impl"; +import { TypeORM } from "./typeorm/typeorm"; +import { encryptionModule } from "@gitpod/gitpod-protocol/lib/encryption/container-module"; +import { KeyProviderImpl, KeyProviderConfig } from "@gitpod/gitpod-protocol/lib/encryption/key-provider"; +import { DBWithTracing, bindDbWithTracing, TracedWorkspaceDB, TracedUserDB, TracedOneTimeSecretDB } from "./traced-db"; +import { OneTimeSecretDB } from "./one-time-secret-db"; +import { DeletedEntryGC } from "./typeorm/deleted-entry-gc"; +import { TypeORMAppInstallationDBImpl } from "./typeorm/app-installation-db-impl"; +import { AppInstallationDB } from "./app-installation-db"; +import { TheiaPluginDBImpl } from "./typeorm/theia-plugin-db-impl"; +import { TheiaPluginDB } from "./theia-plugin-db"; +import { TypeORMOneTimeSecretDBImpl } from "./typeorm/one-time-secret-db-impl"; +import { PendingGithubEventDB, TransactionalPendingGithubEventDBFactory } from "./pending-github-event-db"; +import { + TransactionalPendingGithubEventDBImpl, + TypeORMPendingGithubEventDBImpl, +} from "./typeorm/pending-github-event-db-impl"; +import { GitpodTableDescriptionProvider, TableDescriptionProvider } from "./tables"; +import { PeriodicDbDeleter } from "./periodic-deleter"; +import { TermsAcceptanceDB } from "./terms-acceptance-db"; +import { TermsAcceptanceDBImpl } from "./typeorm/terms-acceptance-db-impl"; +import { CodeSyncResourceDB } from "./typeorm/code-sync-resource-db"; +import { WorkspaceClusterDBImpl } from "./typeorm/workspace-cluster-db-impl"; +import { WorkspaceClusterDB } from "./workspace-cluster-db"; +import { AuthCodeRepositoryDB } from "./typeorm/auth-code-repository-db"; +import { AuthProviderEntryDB } from "./auth-provider-entry-db"; +import { AuthProviderEntryDBImpl } from "./typeorm/auth-provider-entry-db-impl"; +import { TeamSubscriptionDB } from "./team-subscription-db"; +import { AccountingDB, TransactionalAccountingDBFactory } from "./accounting-db"; +import { EmailDomainFilterDB } from "./email-domain-filter-db"; +import { EduEmailDomainDB } from "./edu-email-domain-db"; +import { EMailDB } from "./email-db"; +import { LicenseDB } from "./license-db"; +import { LicenseDBImpl } from "./typeorm/license-db-impl"; +import { TypeORMEMailDBImpl } from "./typeorm/email-db-impl"; +import { EduEmailDomainDBImpl } from "./typeorm/edu-email-domain-db-impl"; +import { EmailDomainFilterDBImpl } from "./typeorm/email-domain-filter-db-impl"; +import { TeamSubscriptionDBImpl } from "./typeorm/team-subscription-db-impl"; +import { TransactionalAccountingDBImpl, TypeORMAccountingDBImpl } from "./typeorm/accounting-db-impl"; +import { TeamDB } from "./team-db"; +import { TeamDBImpl } from "./typeorm/team-db-impl"; +import { ProjectDB } from "./project-db"; +import { ProjectDBImpl } from "./typeorm/project-db-impl"; +import { EntityManager } from "typeorm"; +import { OssAllowListDB } from "./oss-allowlist-db"; +import { OssAllowListDBImpl } from "./typeorm/oss-allowlist-db-impl"; +import { TypeORMInstallationAdminImpl } from "./typeorm/installation-admin-db-impl"; +import { InstallationAdminDB } from "./installation-admin-db"; // THE DB container module that contains all DB implementations export const dbContainerModule = new ContainerModule((bind, unbind, isBound, rebind) => { @@ -101,19 +104,21 @@ export const dbContainerModule = new ContainerModule((bind, unbind, isBound, reb bind(TypeORMPendingGithubEventDBImpl).toSelf().inSingletonScope(); bind(PendingGithubEventDB).toService(TypeORMPendingGithubEventDBImpl); - bind(TransactionalPendingGithubEventDBFactory).toFactory(ctx => { + bind(TransactionalPendingGithubEventDBFactory).toFactory((ctx) => { return (manager: EntityManager) => { return new TransactionalPendingGithubEventDBImpl(manager); - } + }; }); encryptionModule(bind, unbind, isBound, rebind); - bind(KeyProviderConfig).toDynamicValue(ctx => { - const config = ctx.container.get(Config); - return { - keys: KeyProviderImpl.loadKeyConfigFromJsonString(config.dbEncryptionKeys) - }; - }).inSingletonScope(); + bind(KeyProviderConfig) + .toDynamicValue((ctx) => { + const config = ctx.container.get(Config); + return { + keys: KeyProviderImpl.loadKeyConfigFromJsonString(config.dbEncryptionKeys), + }; + }) + .inSingletonScope(); bind(GitpodTableDescriptionProvider).toSelf().inSingletonScope(); bind(TableDescriptionProvider).toService(GitpodTableDescriptionProvider); @@ -132,10 +137,10 @@ export const dbContainerModule = new ContainerModule((bind, unbind, isBound, reb // com concerns bind(AccountingDB).to(TypeORMAccountingDBImpl).inSingletonScope(); - bind(TransactionalAccountingDBFactory).toFactory(ctx => { + bind(TransactionalAccountingDBFactory).toFactory((ctx) => { return (manager: EntityManager) => { return new TransactionalAccountingDBImpl(manager); - } + }; }); bind(TeamSubscriptionDB).to(TeamSubscriptionDBImpl).inSingletonScope(); bind(EmailDomainFilterDB).to(EmailDomainFilterDBImpl).inSingletonScope(); diff --git a/components/gitpod-db/src/edu-email-domain-db.ts b/components/gitpod-db/src/edu-email-domain-db.ts index 51408691af6feb..380c4062c6f306 100644 --- a/components/gitpod-db/src/edu-email-domain-db.ts +++ b/components/gitpod-db/src/edu-email-domain-db.ts @@ -6,9 +6,9 @@ import { EduEmailDomain } from "@gitpod/gitpod-protocol"; -export const EduEmailDomainDB = Symbol('EduEmailDomainDB'); +export const EduEmailDomainDB = Symbol("EduEmailDomainDB"); export interface EduEmailDomainDB { storeDomainEntry(domain: EduEmailDomain): Promise; readEducationalInstitutionDomains(): Promise; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/email-db.ts b/components/gitpod-db/src/email-db.ts index 308ceb39241eac..fbee658332b1f4 100644 --- a/components/gitpod-db/src/email-db.ts +++ b/components/gitpod-db/src/email-db.ts @@ -10,7 +10,7 @@ import { DBEmail } from "./typeorm/entity/db-email"; export type PartialEMailUpdate = DeepPartial & Pick; -export const EMailDB = Symbol('EMailDB'); +export const EMailDB = Symbol("EMailDB"); export interface EMailDB { scheduleEmail(newEmail: EMail): Promise; updatePartial(partial: PartialEMailUpdate): Promise; diff --git a/components/gitpod-db/src/email-domain-filter-db.ts b/components/gitpod-db/src/email-domain-filter-db.ts index 4123b75f2b2e02..a7615fd64f4033 100644 --- a/components/gitpod-db/src/email-domain-filter-db.ts +++ b/components/gitpod-db/src/email-domain-filter-db.ts @@ -6,9 +6,9 @@ import { EmailDomainFilterEntry } from "@gitpod/gitpod-protocol"; -export const EmailDomainFilterDB = Symbol('EmailDomainFilterDB'); +export const EmailDomainFilterDB = Symbol("EmailDomainFilterDB"); export interface EmailDomainFilterDB { storeFilterEntry(entry: EmailDomainFilterEntry): Promise; filter(emailDomain: string): Promise; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/installation-admin-db.ts b/components/gitpod-db/src/installation-admin-db.ts index a23fefd0810a80..c116def0c6e954 100644 --- a/components/gitpod-db/src/installation-admin-db.ts +++ b/components/gitpod-db/src/installation-admin-db.ts @@ -6,7 +6,7 @@ import { InstallationAdmin, InstallationAdminSettings } from "@gitpod/gitpod-protocol"; -export const InstallationAdminDB = Symbol('InstallationAdminDB'); +export const InstallationAdminDB = Symbol("InstallationAdminDB"); export interface InstallationAdminDB { getData(): Promise; setSettings(settings: Partial): Promise; diff --git a/components/gitpod-db/src/license-db.ts b/components/gitpod-db/src/license-db.ts index 2934d56ae6c900..088d1afed87c3d 100644 --- a/components/gitpod-db/src/license-db.ts +++ b/components/gitpod-db/src/license-db.ts @@ -4,9 +4,9 @@ * See License-AGPL.txt in the project root for license information. */ -export const LicenseDB = Symbol('LicenseDB'); +export const LicenseDB = Symbol("LicenseDB"); export interface LicenseDB { - store(id: string, key: string): Promise - get(): Promise -} \ No newline at end of file + store(id: string, key: string): Promise; + get(): Promise; +} diff --git a/components/gitpod-db/src/migrate-migrations.ts b/components/gitpod-db/src/migrate-migrations.ts index e6ff145b9cf891..3dffd8c3c01aa7 100644 --- a/components/gitpod-db/src/migrate-migrations.ts +++ b/components/gitpod-db/src/migrate-migrations.ts @@ -4,12 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ - import { TypeORM } from "./typeorm/typeorm"; import { Config } from "./config"; import { MigrateMigrations0_2_0 } from "./typeorm/migrate-migrations-0_2_0"; - async function migrateMigrationsTable() { const config = new Config(); const typeorm = new TypeORM(config, {}); diff --git a/components/gitpod-db/src/one-time-secret-db.ts b/components/gitpod-db/src/one-time-secret-db.ts index d6d05d4f736c9f..4ce34c50756219 100644 --- a/components/gitpod-db/src/one-time-secret-db.ts +++ b/components/gitpod-db/src/one-time-secret-db.ts @@ -4,11 +4,9 @@ * See License-AGPL.txt in the project root for license information. */ - export const OneTimeSecretDB = Symbol("OneTimeSecretDB"); export interface OneTimeSecretDB { - /** * Register registers a secret for one-time retrieval until a certain time. * @@ -38,5 +36,4 @@ export interface OneTimeSecretDB { * Prune delets all expired one-time secretes. */ pruneExpired(): Promise; - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/oss-allowlist-db.ts b/components/gitpod-db/src/oss-allowlist-db.ts index 3f1feff72e210e..5e189327f05786 100644 --- a/components/gitpod-db/src/oss-allowlist-db.ts +++ b/components/gitpod-db/src/oss-allowlist-db.ts @@ -8,5 +8,5 @@ export const OssAllowListDB = Symbol("OssAllowListDB"); export interface OssAllowListDB { delete(identity: string): Promise; - hasAny(identities: string[]): Promise -} \ No newline at end of file + hasAny(identities: string[]): Promise; +} diff --git a/components/gitpod-db/src/pending-github-event-db.ts b/components/gitpod-db/src/pending-github-event-db.ts index a5fb4fbc67e429..27d589c7125273 100644 --- a/components/gitpod-db/src/pending-github-event-db.ts +++ b/components/gitpod-db/src/pending-github-event-db.ts @@ -9,12 +9,12 @@ import { EntityManager } from "typeorm"; export type PendingGithubEventWithUser = PendingGithubEvent & { identity: Identity & { user: User } }; -export const TransactionalPendingGithubEventDBFactory = Symbol('TransactionalPendingGithubEventDBFactory'); +export const TransactionalPendingGithubEventDBFactory = Symbol("TransactionalPendingGithubEventDBFactory"); export interface TransactionalPendingGithubEventDBFactory { (manager: EntityManager): PendingGithubEventDB; } -export const PendingGithubEventDB = Symbol('PendingGithubEventDB'); +export const PendingGithubEventDB = Symbol("PendingGithubEventDB"); export interface PendingGithubEventDB { store(evt: PendingGithubEvent): Promise; findByGithubUserID(type: string, accountId: number): Promise; @@ -26,6 +26,5 @@ export interface PendingGithubEventDB { */ findWithUser(type: string): Promise; - transaction(code: (db: PendingGithubEventDB) => Promise): Promise; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/periodic-deleter.ts b/components/gitpod-db/src/periodic-deleter.ts index ee3a11fd33baa4..a582a52d26cef9 100644 --- a/components/gitpod-db/src/periodic-deleter.ts +++ b/components/gitpod-db/src/periodic-deleter.ts @@ -5,8 +5,7 @@ */ import { inject, injectable } from "inversify"; -import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; - +import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { GitpodTableDescriptionProvider, TableDescription } from "./tables"; import { TypeORM } from "./typeorm/typeorm"; @@ -14,19 +13,18 @@ import { repeat } from "@gitpod/gitpod-protocol/lib/util/repeat"; @injectable() export class PeriodicDbDeleter { - @inject(GitpodTableDescriptionProvider) protected readonly tableProvider: GitpodTableDescriptionProvider; @inject(TypeORM) protected readonly typeORM: TypeORM; start() { - log.error("[PeriodicDbDeleter] Start ...") - this.sync().catch(err => log.error("[PeriodicDbDeleter] sync failed", err)); + log.error("[PeriodicDbDeleter] Start ..."); + this.sync().catch((err) => log.error("[PeriodicDbDeleter] sync failed", err)); } protected async sync() { - const doSync = async() => { + const doSync = async () => { const sortedTables = this.tableProvider.getSortedTables(); - const toBeDeleted: { table: string, deletions: string[] }[] = []; + const toBeDeleted: { table: string; deletions: string[] }[] = []; for (const table of sortedTables) { toBeDeleted.push(await this.collectRowsToBeDeleted(table)); } @@ -34,14 +32,16 @@ export class PeriodicDbDeleter { const pendingDeletions: Promise[] = []; for (const { deletions } of toBeDeleted.reverse()) { for (const deletion of deletions) { - pendingDeletions.push(this.query(deletion).catch(err => log.error(`[PeriodicDbDeleter] sync error`, err))); + pendingDeletions.push( + this.query(deletion).catch((err) => log.error(`[PeriodicDbDeleter] sync error`, err)), + ); } } await Promise.all(pendingDeletions); }; - repeat(doSync, 30000); // deletion is never time-critical, so we should ensure we do not spam ourselves + repeat(doSync, 30000); // deletion is never time-critical, so we should ensure we do not spam ourselves } - protected async collectRowsToBeDeleted(table: TableDescription): Promise<{ table: string, deletions: string[] }> { + protected async collectRowsToBeDeleted(table: TableDescription): Promise<{ table: string; deletions: string[] }> { try { await this.query(`SELECT COUNT(1) FROM ${table.name}`); } catch (err) { @@ -56,11 +56,13 @@ export class PeriodicDbDeleter { } const { deletionColumn, primaryKeys } = table; - const markedAsDeletedQuery = `SELECT ${primaryKeys.join(', ')} FROM ${table.name} WHERE ${deletionColumn} = true ;`; + const markedAsDeletedQuery = `SELECT ${primaryKeys.join(", ")} FROM ${ + table.name + } WHERE ${deletionColumn} = true ;`; const rows = await this.query(markedAsDeletedQuery); - const whereClauseFn = (row: any) => primaryKeys.map(pk => `${pk}='${row[pk]}'`).join(" AND "); - for(const i in rows) { + const whereClauseFn = (row: any) => primaryKeys.map((pk) => `${pk}='${row[pk]}'`).join(" AND "); + for (const i in rows) { const row = rows[i]; const whereClause = whereClauseFn(row); deletions.push(`DELETE FROM ${table.name} WHERE ${whereClause};`); @@ -78,6 +80,4 @@ export class PeriodicDbDeleter { protected get connection() { return this.typeORM.getConnection(); } - } - diff --git a/components/gitpod-db/src/project-db.spec.db.ts b/components/gitpod-db/src/project-db.spec.db.ts index dc0b62ac7fd3a2..9c3b09d553f9a7 100644 --- a/components/gitpod-db/src/project-db.spec.db.ts +++ b/components/gitpod-db/src/project-db.spec.db.ts @@ -4,18 +4,19 @@ * See License-AGPL.txt in the project root for license information. */ -import * as chai from 'chai'; +import * as chai from "chai"; const expect = chai.expect; -import { suite, test } from 'mocha-typescript'; -import { TypeORM } from './typeorm/typeorm'; -import { TypeORMUserDBImpl } from './typeorm/user-db-impl'; -import { testContainer } from './test-container'; -import { ProjectDBImpl } from './typeorm/project-db-impl'; -import { DBProject } from './typeorm/entity/db-project'; -import { DBUser } from './typeorm/entity/db-user'; +import { suite, test } from "mocha-typescript"; +import { TypeORM } from "./typeorm/typeorm"; +import { TypeORMUserDBImpl } from "./typeorm/user-db-impl"; +import { testContainer } from "./test-container"; +import { ProjectDBImpl } from "./typeorm/project-db-impl"; +import { DBProject } from "./typeorm/entity/db-project"; +import { DBUser } from "./typeorm/entity/db-user"; import { Project } from "@gitpod/gitpod-protocol"; -@suite class ProjectDBSpec { +@suite +class ProjectDBSpec { projectDb = testContainer.get(ProjectDBImpl); userDb = testContainer.get(TypeORMUserDBImpl); @@ -30,18 +31,27 @@ import { Project } from "@gitpod/gitpod-protocol"; async wipeRepo() { const typeorm = testContainer.get(TypeORM); const manager = await typeorm.getConnection(); - await manager.getRepository(DBProject).delete({}) + await manager.getRepository(DBProject).delete({}); await manager.getRepository(DBUser).delete({}); } @test() public async findProjectBySearchTerm() { const user = await this.userDb.newUser(); - user.identities.push({ authProviderId: 'GitHub', authId: '1234', authName: 'newUser', primaryEmail: 'newuser@git.com' }); + user.identities.push({ + authProviderId: "GitHub", + authId: "1234", + authName: "newUser", + primaryEmail: "newuser@git.com", + }); await this.userDb.storeUser(user); const project = Project.create({ - name: "some-project", slug: "some-project", cloneUrl: "some-random-clone-url", userId: user.id, appInstallationId: "app-1" + name: "some-project", + slug: "some-project", + cloneUrl: "some-random-clone-url", + userId: user.id, + appInstallationId: "app-1", }); const searchTerm = "rand"; const storedProject = await this.projectDb.storeProject(project); @@ -51,4 +61,4 @@ import { Project } from "@gitpod/gitpod-protocol"; } } -module.exports = new ProjectDBSpec() +module.exports = new ProjectDBSpec(); diff --git a/components/gitpod-db/src/project-db.ts b/components/gitpod-db/src/project-db.ts index 5fbeb551e28216..5f26d2e727694e 100644 --- a/components/gitpod-db/src/project-db.ts +++ b/components/gitpod-db/src/project-db.ts @@ -6,14 +6,20 @@ import { PartialProject, Project, ProjectEnvVar, ProjectEnvVarWithValue } from "@gitpod/gitpod-protocol"; -export const ProjectDB = Symbol('ProjectDB'); +export const ProjectDB = Symbol("ProjectDB"); export interface ProjectDB { findProjectById(projectId: string): Promise; findProjectByCloneUrl(cloneUrl: string): Promise; findProjectsByCloneUrls(cloneUrls: string[]): Promise<(Project & { teamOwners?: string[] })[]>; findTeamProjects(teamId: string): Promise; findUserProjects(userId: string): Promise; - findProjectsBySearchTerm(offset: number, limit: number, orderBy: keyof Project, orderDir: "ASC" | "DESC", searchTerm: string): Promise<{ total: number, rows: Project[] }>; + findProjectsBySearchTerm( + offset: number, + limit: number, + orderBy: keyof Project, + orderDir: "ASC" | "DESC", + searchTerm: string, + ): Promise<{ total: number; rows: Project[] }>; storeProject(project: Project): Promise; updateProject(partialProject: PartialProject): Promise; markDeleted(projectId: string): Promise; diff --git a/components/gitpod-db/src/tables.ts b/components/gitpod-db/src/tables.ts index 6d61e77a5cd303..2d7b8c45a3f035 100644 --- a/components/gitpod-db/src/tables.ts +++ b/components/gitpod-db/src/tables.ts @@ -26,7 +26,7 @@ export interface TableDescriptionProvider { getSortedTables(): TableDescription[]; } -export const TableDescriptionProvider = Symbol('TableDescriptionProvider'); +export const TableDescriptionProvider = Symbol("TableDescriptionProvider"); @injectable() export class GitpodSessionTableDescriptionProvider implements TableDescriptionProvider { @@ -35,13 +35,12 @@ export class GitpodSessionTableDescriptionProvider implements TableDescriptionPr return [ { name: "sessions", - primaryKeys: ['session_id'], - timeColumn: '_lastModified', - expiryColumn: 'expires' - } - ] + primaryKeys: ["session_id"], + timeColumn: "_lastModified", + expiryColumn: "expires", + }, + ]; } - } /** @@ -55,207 +54,207 @@ export class GitpodTableDescriptionProvider implements TableDescriptionProvider readonly name = "gitpod"; protected readonly tables: TableDescription[] = [ { - name: 'd_b_account_entry', - primaryKeys: ['uid'], - timeColumn: '_lastModified' + name: "d_b_account_entry", + primaryKeys: ["uid"], + timeColumn: "_lastModified", }, { - name: 'd_b_subscription', - primaryKeys: ['uid'], - timeColumn: '_lastModified', - dependencies: ['d_b_user'] + name: "d_b_subscription", + primaryKeys: ["uid"], + timeColumn: "_lastModified", + dependencies: ["d_b_user"], }, { - name: 'd_b_subscription_additional_data', - primaryKeys: ['paymentReference'], - timeColumn: 'lastModified' + name: "d_b_subscription_additional_data", + primaryKeys: ["paymentReference"], + timeColumn: "lastModified", }, { - name: 'd_b_identity', - primaryKeys: ['authProviderId', 'authId'], - timeColumn: '_lastModified', - deletionColumn: 'deleted', - dependencies: ['d_b_user'] + name: "d_b_identity", + primaryKeys: ["authProviderId", "authId"], + timeColumn: "_lastModified", + deletionColumn: "deleted", + dependencies: ["d_b_user"], }, { - name: 'd_b_user', - primaryKeys: ['id'], - timeColumn: '_lastModified' + name: "d_b_user", + primaryKeys: ["id"], + timeColumn: "_lastModified", }, { - name: 'd_b_user_message_view_entry', - primaryKeys: ['id'], - timeColumn: 'viewedAt', - dependencies: ['d_b_user'] + name: "d_b_user_message_view_entry", + primaryKeys: ["id"], + timeColumn: "viewedAt", + dependencies: ["d_b_user"], }, { - name: 'd_b_user_storage_resource', - primaryKeys: ['id'], - timeColumn: '_lastModified', - deletionColumn: 'deleted', - dependencies: ['d_b_user'] + name: "d_b_user_storage_resource", + primaryKeys: ["id"], + timeColumn: "_lastModified", + deletionColumn: "deleted", + dependencies: ["d_b_user"], }, { - name: 'd_b_workspace', - primaryKeys: ['id'], - timeColumn: '_lastModified', - deletionColumn: 'deleted', - dependencies: ['d_b_user'] + name: "d_b_workspace", + primaryKeys: ["id"], + timeColumn: "_lastModified", + deletionColumn: "deleted", + dependencies: ["d_b_user"], }, { - name: 'd_b_workspace_instance', - primaryKeys: ['id'], - timeColumn: '_lastModified', - dependencies: ['d_b_workspace'], - deletionColumn: 'deleted', - ignoreColumns: ['phase'] + name: "d_b_workspace_instance", + primaryKeys: ["id"], + timeColumn: "_lastModified", + dependencies: ["d_b_workspace"], + deletionColumn: "deleted", + ignoreColumns: ["phase"], }, { - name: 'd_b_workspace_instance_user', - primaryKeys: ['instanceId', 'userId'], - timeColumn: '_lastModified', - dependencies: ['d_b_workspace_instance', 'd_b_user'] + name: "d_b_workspace_instance_user", + primaryKeys: ["instanceId", "userId"], + timeColumn: "_lastModified", + dependencies: ["d_b_workspace_instance", "d_b_user"], }, { - name: 'd_b_workspace_report_entry', - primaryKeys: ['uid'], - timeColumn: 'time', - dependencies: ['d_b_workspace'] + name: "d_b_workspace_report_entry", + primaryKeys: ["uid"], + timeColumn: "time", + dependencies: ["d_b_workspace"], }, { - name: 'd_b_snapshot', - primaryKeys: ['id'], - timeColumn: 'creationTime', - dependencies: ['d_b_workspace'] + name: "d_b_snapshot", + primaryKeys: ["id"], + timeColumn: "creationTime", + dependencies: ["d_b_workspace"], }, { - name: 'd_b_email_domain_filter', - primaryKeys: ['domain'], - timeColumn: '_lastModified' + name: "d_b_email_domain_filter", + primaryKeys: ["domain"], + timeColumn: "_lastModified", }, { - name: 'd_b_prebuilt_workspace', - primaryKeys: ['id'], - timeColumn: '_lastModified' + name: "d_b_prebuilt_workspace", + primaryKeys: ["id"], + timeColumn: "_lastModified", }, { - name: 'd_b_app_installation', - primaryKeys: ['platform', 'installationID', 'state'], - timeColumn: 'creationTime' + name: "d_b_app_installation", + primaryKeys: ["platform", "installationID", "state"], + timeColumn: "creationTime", }, { - name: 'd_b_token_entry', - primaryKeys: ['uid'], - deletionColumn: 'deleted', - timeColumn: '_lastModified' + name: "d_b_token_entry", + primaryKeys: ["uid"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_team_subscription', - primaryKeys: ['id'], - timeColumn: '_lastModified' + name: "d_b_team_subscription", + primaryKeys: ["id"], + timeColumn: "_lastModified", }, { - name: 'd_b_team_subscription_slot', - primaryKeys: ['id'], - timeColumn: '_lastModified' + name: "d_b_team_subscription_slot", + primaryKeys: ["id"], + timeColumn: "_lastModified", }, { - name: 'd_b_edu_email_domain', - primaryKeys: ['domain'], - timeColumn: '_lastModified' + name: "d_b_edu_email_domain", + primaryKeys: ["domain"], + timeColumn: "_lastModified", }, { - name: 'd_b_theia_plugin', - primaryKeys: ['id'], - timeColumn: '_lastModified' + name: "d_b_theia_plugin", + primaryKeys: ["id"], + timeColumn: "_lastModified", }, { - name: 'd_b_user_env_var', - primaryKeys: ['id', 'userId'], - timeColumn: '_lastModified' + name: "d_b_user_env_var", + primaryKeys: ["id", "userId"], + timeColumn: "_lastModified", }, { - name: 'd_b_email', - primaryKeys: ['uid'], - timeColumn: '_lastModified' + name: "d_b_email", + primaryKeys: ["uid"], + timeColumn: "_lastModified", }, { - name: 'd_b_gitpod_token', - primaryKeys: ['tokenHash'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', - dependencies: ['d_b_user'], + name: "d_b_gitpod_token", + primaryKeys: ["tokenHash"], + deletionColumn: "deleted", + timeColumn: "_lastModified", + dependencies: ["d_b_user"], }, { - name: 'd_b_one_time_secret', - primaryKeys: ['id'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_one_time_secret", + primaryKeys: ["id"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_auth_provider_entry', - primaryKeys: ['id'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_auth_provider_entry", + primaryKeys: ["id"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_code_sync_resource', - primaryKeys: ['userId','kind','rev'], - deletionColumn: 'deleted', - timeColumn: 'created', + name: "d_b_code_sync_resource", + primaryKeys: ["userId", "kind", "rev"], + deletionColumn: "deleted", + timeColumn: "created", }, { - name: 'd_b_terms_acceptance_entry', - primaryKeys: ['userId'], - timeColumn: '_lastModified', + name: "d_b_terms_acceptance_entry", + primaryKeys: ["userId"], + timeColumn: "_lastModified", }, { - name: 'd_b_team', - primaryKeys: ['id'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_team", + primaryKeys: ["id"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_team_membership', - primaryKeys: ['id'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_team_membership", + primaryKeys: ["id"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_team_membership_invite', - primaryKeys: ['id'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_team_membership_invite", + primaryKeys: ["id"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_project', - primaryKeys: ['id'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_project", + primaryKeys: ["id"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_prebuild_info', - primaryKeys: ['prebuildId'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_prebuild_info", + primaryKeys: ["prebuildId"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_oss_allow_list', - primaryKeys: ['identity'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_oss_allow_list", + primaryKeys: ["identity"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_project_env_var', - primaryKeys: ['id', 'projectId'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_project_env_var", + primaryKeys: ["id", "projectId"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, { - name: 'd_b_project_info', - primaryKeys: ['projectId'], - deletionColumn: 'deleted', - timeColumn: '_lastModified', + name: "d_b_project_info", + primaryKeys: ["projectId"], + deletionColumn: "deleted", + timeColumn: "_lastModified", }, /** * BEWARE @@ -263,26 +262,29 @@ export class GitpodTableDescriptionProvider implements TableDescriptionProvider * When updating this list, make sure you update the deleted-entry-gc.ts in gitpod-db * as well, if you're adding a table that needs some of its entries deleted. */ - ] + ]; public getSortedTables(): TableDescription[] { - return new TopologicalSort().sort(this.tables, t => t.name, t => (t.dependencies || []).map(e => this.tables.find(te => te.name == e)!)); + return new TopologicalSort().sort( + this.tables, + (t) => t.name, + (t) => (t.dependencies || []).map((e) => this.tables.find((te) => te.name == e)!), + ); } - } class TopologicalSort { protected result: E[] = []; protected visitedNodes: Map; - public sort(values: E[], key: (e: E)=>K, edge: (e: E)=>E[]): E[] { + public sort(values: E[], key: (e: E) => K, edge: (e: E) => E[]): E[] { this.visitedNodes = new Map(); this.result = []; - for(const e of values) { + for (const e of values) { const k = key(e); const priorVisit = this.visitedNodes.get(k); - if(priorVisit === undefined) { + if (priorVisit === undefined) { this.visit(e, key, edge); } } @@ -290,14 +292,14 @@ class TopologicalSort { return this.result; } - protected visit(e: E, key: (e: E)=>K, edges: (e: E)=>E[]) { - if(this.isMarkedPermanently(key(e))) return; - if(this.isMarkedTemporarily(key(e))) { + protected visit(e: E, key: (e: E) => K, edges: (e: E) => E[]) { + if (this.isMarkedPermanently(key(e))) return; + if (this.isMarkedTemporarily(key(e))) { throw new Error("Circle detected in " + key); } this.visitedNodes.set(key(e), false); - edges(e).forEach(edge => this.visit(edge, key, edges)); + edges(e).forEach((edge) => this.visit(edge, key, edges)); this.visitedNodes.set(key(e), true); this.result.push(e); } @@ -313,5 +315,4 @@ class TopologicalSort { protected isUnmarked(k: K) { return this.visitedNodes.get(k) === undefined; } - } diff --git a/components/gitpod-db/src/team-db.spec.db.ts b/components/gitpod-db/src/team-db.spec.db.ts index 24e69932dea243..0afc9b076fa02b 100644 --- a/components/gitpod-db/src/team-db.spec.db.ts +++ b/components/gitpod-db/src/team-db.spec.db.ts @@ -4,21 +4,21 @@ * See License.enterprise.txt in the project root folder. */ - import * as chai from 'chai'; - const expect = chai.expect; -import { suite, test, timeout } from 'mocha-typescript'; - -import { testContainer } from './test-container'; -import { TeamDBImpl } from './typeorm/team-db-impl'; -import { TypeORMUserDBImpl } from './typeorm/user-db-impl'; -import { TypeORM } from './typeorm/typeorm'; -import { DBTeam } from './typeorm/entity/db-team'; -import { DBTeamMembership } from './typeorm/entity/db-team-membership'; -import { DBUser } from './typeorm/entity/db-user'; -import { DBIdentity } from './typeorm/entity/db-identity'; - -@suite class TeamDBSpec { - +import * as chai from "chai"; +const expect = chai.expect; +import { suite, test, timeout } from "mocha-typescript"; + +import { testContainer } from "./test-container"; +import { TeamDBImpl } from "./typeorm/team-db-impl"; +import { TypeORMUserDBImpl } from "./typeorm/user-db-impl"; +import { TypeORM } from "./typeorm/typeorm"; +import { DBTeam } from "./typeorm/entity/db-team"; +import { DBTeamMembership } from "./typeorm/entity/db-team-membership"; +import { DBUser } from "./typeorm/entity/db-user"; +import { DBIdentity } from "./typeorm/entity/db-identity"; + +@suite +class TeamDBSpec { db = testContainer.get(TeamDBImpl); userDb = testContainer.get(TypeORMUserDBImpl); @@ -44,69 +44,97 @@ import { DBIdentity } from './typeorm/entity/db-identity'; const user = await this.userDb.newUser(); let dbResult = await this.db.findTeamsByUser(user.id); expect(dbResult.length).to.eq(0); - await this.db.createTeam(user.id, 'Ground Control'); + await this.db.createTeam(user.id, "Ground Control"); dbResult = await this.db.findTeamsByUser(user.id); expect(dbResult.length).to.eq(1); - expect(dbResult[0].name).to.eq('Ground Control'); + expect(dbResult[0].name).to.eq("Ground Control"); } @test(timeout(10000)) public async findTeamMembers() { const user = await this.userDb.newUser(); - user.identities.push({ authProviderId: 'GitHub', authId: '1234', authName: 'Major Tom', primaryEmail: 'tom@example.com' }); + user.identities.push({ + authProviderId: "GitHub", + authId: "1234", + authName: "Major Tom", + primaryEmail: "tom@example.com", + }); await this.userDb.storeUser(user); - const team = await this.db.createTeam(user.id, 'Flight Crew'); + const team = await this.db.createTeam(user.id, "Flight Crew"); const members = await this.db.findMembersByTeam(team.id); expect(members.length).to.eq(1); expect(members[0].userId).to.eq(user.id); - expect(members[0].primaryEmail).to.eq('tom@example.com'); + expect(members[0].primaryEmail).to.eq("tom@example.com"); } @test(timeout(15000)) public async findTeamWhenUserIsSoleOwner() { const user = await this.userDb.newUser(); - user.identities.push({ authProviderId: 'GitHub', authId: '2345', authName: 'Nana', primaryEmail: 'nana@example.com' }); + user.identities.push({ + authProviderId: "GitHub", + authId: "2345", + authName: "Nana", + primaryEmail: "nana@example.com", + }); await this.userDb.storeUser(user); - const ownTeam = await this.db.createTeam(user.id, 'My Own Team'); + const ownTeam = await this.db.createTeam(user.id, "My Own Team"); const teams = await this.db.findTeamsByUserAsSoleOwner(user.id); expect(teams.length).to.eq(1); expect(teams[0].id).to.eq(ownTeam.id); - } @test(timeout(10000)) public async findTeamWhenUserIsSoleOwnerWithMembers() { const user = await this.userDb.newUser(); - user.identities.push({ authProviderId: 'GitHub', authId: '2345', authName: 'Nana', primaryEmail: 'nana@example.com' }); + user.identities.push({ + authProviderId: "GitHub", + authId: "2345", + authName: "Nana", + primaryEmail: "nana@example.com", + }); await this.userDb.storeUser(user); const user2 = await this.userDb.newUser(); - user2.identities.push({ authProviderId: 'GitLab', authId: '4567', authName: 'Dudu', primaryEmail: 'dudu@example.com' }); + user2.identities.push({ + authProviderId: "GitLab", + authId: "4567", + authName: "Dudu", + primaryEmail: "dudu@example.com", + }); await this.userDb.storeUser(user2); - const ownTeam = await this.db.createTeam(user.id, 'My Own Team With Members'); + const ownTeam = await this.db.createTeam(user.id, "My Own Team With Members"); await this.db.addMemberToTeam(user2.id, ownTeam.id); const teams = await this.db.findTeamsByUserAsSoleOwner(user.id); expect(teams.length).to.eq(1); expect(teams[0].id).to.eq(ownTeam.id); - } @test(timeout(10000)) public async findNoTeamWhenCoOwned() { const user = await this.userDb.newUser(); - user.identities.push({ authProviderId: 'GitHub', authId: '2345', authName: 'Nana', primaryEmail: 'nana@example.com' }); + user.identities.push({ + authProviderId: "GitHub", + authId: "2345", + authName: "Nana", + primaryEmail: "nana@example.com", + }); await this.userDb.storeUser(user); const user2 = await this.userDb.newUser(); - user2.identities.push({ authProviderId: 'GitLab', authId: '4567', authName: 'Dudu', primaryEmail: 'dudu@example.com' }); + user2.identities.push({ + authProviderId: "GitLab", + authId: "4567", + authName: "Dudu", + primaryEmail: "dudu@example.com", + }); await this.userDb.storeUser(user2); - const jointTeam = await this.db.createTeam(user.id, 'Joint Team'); + const jointTeam = await this.db.createTeam(user.id, "Joint Team"); await this.db.addMemberToTeam(user2.id, jointTeam.id); - await this.db.setTeamMemberRole(user2.id, jointTeam.id, 'owner'); + await this.db.setTeamMemberRole(user2.id, jointTeam.id, "owner"); const teams = await this.db.findTeamsByUserAsSoleOwner(user.id); @@ -116,13 +144,13 @@ import { DBIdentity } from './typeorm/entity/db-identity'; @test(timeout(10000)) public async findTeams() { const user = await this.userDb.newUser(); - await this.db.createTeam(user.id, 'First Team'); - await this.db.createTeam(user.id, 'Second Team'); + await this.db.createTeam(user.id, "First Team"); + await this.db.createTeam(user.id, "Second Team"); - const searchTerm = 'first'; + const searchTerm = "first"; const result = await this.db.findTeams(0, 10, "creationTime", "DESC", searchTerm); expect(result.rows.length).to.eq(1); } } -module.exports = new TeamDBSpec() +module.exports = new TeamDBSpec(); diff --git a/components/gitpod-db/src/team-db.ts b/components/gitpod-db/src/team-db.ts index 158e30093e23ff..84ef1db2122608 100644 --- a/components/gitpod-db/src/team-db.ts +++ b/components/gitpod-db/src/team-db.ts @@ -6,9 +6,15 @@ import { Team, TeamMemberInfo, TeamMemberRole, TeamMembershipInvite } from "@gitpod/gitpod-protocol"; -export const TeamDB = Symbol('TeamDB'); +export const TeamDB = Symbol("TeamDB"); export interface TeamDB { - findTeams(offset: number, limit: number, orderBy: keyof Team, orderDir: "ASC" | "DESC", searchTerm: string): Promise<{ total: number, rows: Team[] }>; + findTeams( + offset: number, + limit: number, + orderBy: keyof Team, + orderDir: "ASC" | "DESC", + searchTerm: string, + ): Promise<{ total: number; rows: Team[] }>; findTeamById(teamId: string): Promise; findMembersByTeam(teamId: string): Promise; findTeamsByUser(userId: string): Promise; diff --git a/components/gitpod-db/src/team-subscription-db.ts b/components/gitpod-db/src/team-subscription-db.ts index d05a181be1740f..e3dc16afb64d64 100644 --- a/components/gitpod-db/src/team-subscription-db.ts +++ b/components/gitpod-db/src/team-subscription-db.ts @@ -7,7 +7,7 @@ import { TeamSubscription, TeamSubscriptionSlot } from "@gitpod/gitpod-protocol/lib/team-subscription-protocol"; import { DeepPartial } from "typeorm"; -export const TeamSubscriptionDB = Symbol('TeamSubscriptionDB'); +export const TeamSubscriptionDB = Symbol("TeamSubscriptionDB"); export interface TeamSubscriptionDB { storeTeamSubscriptionEntry(ts: TeamSubscription): Promise; findTeamSubscriptionById(id: string): Promise; @@ -22,4 +22,4 @@ export interface TeamSubscriptionDB { findSlotsByAssignee(assigneeId: string): Promise; transaction(code: (db: TeamSubscriptionDB) => Promise): Promise; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/terms-acceptance-db.ts b/components/gitpod-db/src/terms-acceptance-db.ts index 0004d5124a91d3..5683dd362aae50 100644 --- a/components/gitpod-db/src/terms-acceptance-db.ts +++ b/components/gitpod-db/src/terms-acceptance-db.ts @@ -6,8 +6,7 @@ import { TermsAcceptanceEntry } from "@gitpod/gitpod-protocol"; - -export const TermsAcceptanceDB = Symbol('TermsAcceptanceDB'); +export const TermsAcceptanceDB = Symbol("TermsAcceptanceDB"); export interface TermsAcceptanceDB { getAcceptedRevision(userId: string): Promise; updateAcceptedRevision(userId: string, revision: string): Promise; diff --git a/components/gitpod-db/src/test-container.ts b/components/gitpod-db/src/test-container.ts index 86489d44724f2a..c95e77ba0d39b1 100644 --- a/components/gitpod-db/src/test-container.ts +++ b/components/gitpod-db/src/test-container.ts @@ -4,8 +4,8 @@ * See License-AGPL.txt in the project root for license information. */ -import { Container } from 'inversify'; -import { dbContainerModule } from './container-module'; +import { Container } from "inversify"; +import { dbContainerModule } from "./container-module"; export const testContainer = new Container(); testContainer.load(dbContainerModule); diff --git a/components/gitpod-db/src/the-big-username-blocklist.d.ts b/components/gitpod-db/src/the-big-username-blocklist.d.ts index 67d70fc549ded3..224de7fedc8df0 100644 --- a/components/gitpod-db/src/the-big-username-blocklist.d.ts +++ b/components/gitpod-db/src/the-big-username-blocklist.d.ts @@ -5,7 +5,7 @@ */ // Source: https://github.com/marteinn/The-Big-Username-Blacklist-JS/blob/master/src/index.js -declare module 'the-big-username-blacklist' { - export function validate (username: string): boolean; +declare module "the-big-username-blacklist" { + export function validate(username: string): boolean; export var list: string[]; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/theia-plugin-db.ts b/components/gitpod-db/src/theia-plugin-db.ts index 2abc74464c4c67..3a911a1d3d2ea2 100644 --- a/components/gitpod-db/src/theia-plugin-db.ts +++ b/components/gitpod-db/src/theia-plugin-db.ts @@ -6,15 +6,20 @@ import { TheiaPlugin } from "@gitpod/gitpod-protocol"; -export const TheiaPluginDB = Symbol('TheiaPluginDB'); +export const TheiaPluginDB = Symbol("TheiaPluginDB"); export interface TheiaPluginDB { - newPlugin(userId: string, pluginName: string, bucketName: string, pathFn: (id: string) => string): Promise; + newPlugin( + userId: string, + pluginName: string, + bucketName: string, + pathFn: (id: string) => string, + ): Promise; storePlugin(plugin: TheiaPlugin): Promise; delete(plugin: TheiaPlugin): Promise; findById(id: string): Promise; findByPluginId(pluginId: string): Promise; - exists(pluginId: string, predicate: { state?: TheiaPlugin.State, hash?: string }): Promise; -} \ No newline at end of file + exists(pluginId: string, predicate: { state?: TheiaPlugin.State; hash?: string }): Promise; +} diff --git a/components/gitpod-db/src/traced-db.ts b/components/gitpod-db/src/traced-db.ts index 37036009d1e625..3d5e2cb3636c8e 100644 --- a/components/gitpod-db/src/traced-db.ts +++ b/components/gitpod-db/src/traced-db.ts @@ -4,9 +4,9 @@ * See License-AGPL.txt in the project root for license information. */ -import { TraceContext, TracingManager } from '@gitpod/gitpod-protocol/lib/util/tracing'; -import { interfaces } from 'inversify'; -import * as opentracing from 'opentracing'; +import { TraceContext, TracingManager } from "@gitpod/gitpod-protocol/lib/util/tracing"; +import { interfaces } from "inversify"; +import * as opentracing from "opentracing"; export class DBWithTracing { protected tracer: opentracing.Tracer; @@ -38,14 +38,14 @@ export class DBWithTracing { } finally { span.finish(); } - } - } + }; + }, }); } } export function bindDbWithTracing(traceKey: string | symbol, bind: interfaces.Bind, delegateKey: string | symbol) { - return bind(traceKey).toDynamicValue(ctx => { + return bind(traceKey).toDynamicValue((ctx) => { const root = ctx.container.get(delegateKey) as T; const tracingManager = ctx.container.get(TracingManager); return new DBWithTracing(root, tracingManager); @@ -55,4 +55,4 @@ export function bindDbWithTracing(traceKey: string | symbol, bind: interfaces export const TracedWorkspaceDB = Symbol("TracedWorkspaceDB"); export const TracedUserDB = Symbol("TracedUserDB"); export const TracedLicenseDB = Symbol("TracedLicenseDB"); -export const TracedOneTimeSecretDB = Symbol("TracedOneTimeSecretDB"); \ No newline at end of file +export const TracedOneTimeSecretDB = Symbol("TracedOneTimeSecretDB"); diff --git a/components/gitpod-db/src/typeorm/accounting-db-impl.ts b/components/gitpod-db/src/typeorm/accounting-db-impl.ts index 7df02643a167df..6aa827f6a0e1a8 100644 --- a/components/gitpod-db/src/typeorm/accounting-db-impl.ts +++ b/components/gitpod-db/src/typeorm/accounting-db-impl.ts @@ -7,27 +7,34 @@ import { AccountingDB, TransactionalAccountingDBFactory } from "../accounting-db"; import { DBAccountEntry } from "./entity/db-account-entry"; import { User } from "@gitpod/gitpod-protocol"; -import { AccountEntry, Subscription, Credit, SubscriptionAndUser } from "@gitpod/gitpod-protocol/lib/accounting-protocol"; +import { + AccountEntry, + Subscription, + Credit, + SubscriptionAndUser, +} from "@gitpod/gitpod-protocol/lib/accounting-protocol"; import { EntityManager, Repository } from "typeorm"; import { DBSubscription, DBSubscriptionAdditionalData } from "./entity/db-subscription"; import { injectable, inject } from "inversify"; -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; import { DBUser } from "../typeorm/entity/db-user"; import { TypeORM } from "./typeorm"; @injectable() export class TypeORMAccountingDBImpl implements AccountingDB { - @inject(TypeORM) typeORM: TypeORM; @inject(TransactionalAccountingDBFactory) protected readonly transactionalFactory: TransactionalAccountingDBFactory; - async transaction(closure: (db: AccountingDB) => Promise, closures?: ((manager: EntityManager) => Promise)[]): Promise { + async transaction( + closure: (db: AccountingDB) => Promise, + closures?: ((manager: EntityManager) => Promise)[], + ): Promise { const manager = await this.getEntityManager(); - return await manager.transaction(async manager => { + return await manager.transaction(async (manager) => { const transactionDB = this.transactionalFactory(manager); const result = await closure(transactionDB); - for (const c of (closures || [])) { + for (const c of closures || []) { await c(manager); } return result; @@ -42,10 +49,10 @@ export class TypeORMAccountingDBImpl implements AccountingDB { return (await this.getEntityManager()).getRepository(DBAccountEntry); } - async newAccountEntry(accountEntry: Omit): Promise { + async newAccountEntry(accountEntry: Omit): Promise { const newEntry = new DBAccountEntry(); AccountEntry.create(newEntry); - Object.assign(newEntry, accountEntry) + Object.assign(newEntry, accountEntry); return await this.storeAccountEntry(newEntry); } @@ -54,12 +61,12 @@ export class TypeORMAccountingDBImpl implements AccountingDB { } async findAccountEntriesFor(userId: string, fromDate: string, toDate: string): Promise { - const query = (await this.getAccountEntryRepo()).createQueryBuilder('entry') + const query = (await this.getAccountEntryRepo()) + .createQueryBuilder("entry") .where("entry.userId = :userId", { userId: userId }) .andWhere("entry.date >= :startDate", { startDate: fromDate }) - .andWhere("entry.date < :endDate", { endDate: toDate }) - return query - .getMany() + .andWhere("entry.date < :endDate", { endDate: toDate }); + return query.getMany(); } /** @@ -68,7 +75,8 @@ export class TypeORMAccountingDBImpl implements AccountingDB { async findOpenCredits(userId: string, date: string): Promise { // TODO ACCOUNTING DB: Review this! const repo = await this.getAccountEntryRepo(); - const rows = await repo.query(` + const rows = await repo.query( + ` SELECT credit.uid, credit.date, credit.expiryDate, @@ -79,18 +87,20 @@ export class TypeORMAccountingDBImpl implements AccountingDB { AND credit.kind = "credit" AND credit.date <= ? ORDER BY expiryDate - `, [userId, date]) + `, + [userId, date], + ); return rows.map((row: any) => { return { uid: row.uid, amount: row.amount, - remainingAmount: row.amount, // Hack works because we do not persist remainingAmount just yet - kind: 'open', // ... and thus all 'credit's are 'open' + remainingAmount: row.amount, // Hack works because we do not persist remainingAmount just yet + kind: "open", // ... and thus all 'credit's are 'open' date: row.date, expiryDate: row.expiryDate ? row.expiryDate : undefined, - description: row.description ? JSON.parse(row.description) : undefined - } - }) + description: row.description ? JSON.parse(row.description) : undefined, + }; + }); } protected async getSubscriptionRepo(): Promise> { @@ -101,17 +111,19 @@ export class TypeORMAccountingDBImpl implements AccountingDB { return (await this.getEntityManager()).getRepository(DBSubscriptionAdditionalData); } - async newSubscription(subscription: Omit): Promise { + async newSubscription(subscription: Omit): Promise { const newSubscription = new DBSubscription(); Subscription.create(newSubscription); - Object.assign(newSubscription, subscription) + Object.assign(newSubscription, subscription); return await this.storeSubscription(newSubscription); } async storeSubscription(subscription: Subscription): Promise { const dbsub = subscription as DBSubscription; if (!dbsub.uid) { - console.warn("Storing subscription without pre-set UUID. Subscriptions should always be created with newSubscription") + console.warn( + "Storing subscription without pre-set UUID. Subscriptions should always be created with newSubscription", + ); dbsub.uid = uuidv4(); } return await (await this.getSubscriptionRepo()).save(dbsub); @@ -127,100 +139,131 @@ export class TypeORMAccountingDBImpl implements AccountingDB { } async findActiveSubscriptionByPlanID(planID: string, date: string): Promise { - return (await this.getSubscriptionRepo()).createQueryBuilder('subscription') - .where('subscription.planID = :planID', { planID }) - .andWhere('subscription.startDate <= :date AND (subscription.endDate = "" OR subscription.endDate > :date)', { date: date }) - .andWhere('subscription.deleted != true') - .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS + return (await this.getSubscriptionRepo()) + .createQueryBuilder("subscription") + .where("subscription.planID = :planID", { planID }) + .andWhere( + 'subscription.startDate <= :date AND (subscription.endDate = "" OR subscription.endDate > :date)', + { date: date }, + ) + .andWhere("subscription.deleted != true") + .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS .getMany(); } async findActiveSubscriptions(fromDate: string, toDate: string): Promise { - return (await this.getSubscriptionRepo()).createQueryBuilder('subscription') - .where('subscription.startDate <= :to AND (subscription.endDate = "" OR subscription.endDate > :from)', - { from: fromDate, to: toDate }) - .andWhere('subscription.deleted != true') - .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS + return (await this.getSubscriptionRepo()) + .createQueryBuilder("subscription") + .where('subscription.startDate <= :to AND (subscription.endDate = "" OR subscription.endDate > :from)', { + from: fromDate, + to: toDate, + }) + .andWhere("subscription.deleted != true") + .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS .getMany(); } async findActiveSubscriptionsForUser(userId: string, date: string): Promise { - return (await this.getSubscriptionRepo()).createQueryBuilder('subscription') - .where('subscription.userId = :userId ', { userId: userId }) - .andWhere('subscription.startDate <= :date AND (subscription.endDate = "" OR subscription.endDate > :date)', { date: date }) - .andWhere('subscription.deleted != true') - .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS + return (await this.getSubscriptionRepo()) + .createQueryBuilder("subscription") + .where("subscription.userId = :userId ", { userId: userId }) + .andWhere( + 'subscription.startDate <= :date AND (subscription.endDate = "" OR subscription.endDate > :date)', + { date: date }, + ) + .andWhere("subscription.deleted != true") + .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS .getMany(); } async findAllSubscriptionsForUser(userId: string): Promise { - return (await this.getSubscriptionRepo()).createQueryBuilder('subscription') + return (await this.getSubscriptionRepo()) + .createQueryBuilder("subscription") .where("subscription.userId = :userId ", { userId: userId }) - .andWhere('subscription.deleted != true') - .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS + .andWhere("subscription.deleted != true") + .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS .orderBy("subscription.startDate", "ASC") .getMany(); } async findSubscriptionsForUserInPeriod(userId: string, fromDate: string, toDate: string): Promise { - return (await this.getSubscriptionRepo()).createQueryBuilder('subscription') - .where('subscription.userId = :userId ', { userId: userId }) - .andWhere('(' + return (await this.getSubscriptionRepo()) + .createQueryBuilder("subscription") + .where("subscription.userId = :userId ", { userId: userId }) + .andWhere( + "(" + // Partial overlaps: start OR end internal - + '(subscription.startDate >= :from AND subscription.startDate <= :to)' - + ' OR (subscription.endDate != "" AND subscription.endDate > :from AND subscription.endDate < :to)' + "(subscription.startDate >= :from AND subscription.startDate <= :to)" + + ' OR (subscription.endDate != "" AND subscription.endDate > :from AND subscription.endDate < :to)' + // Complete overlap: start AND end external - + ' OR (subscription.startDate < :from AND (subscription.endDate = "" OR subscription.endDate > :to))' - + ')', - { from: fromDate, to: toDate }) - .andWhere('subscription.deleted != true') - .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS + ' OR (subscription.startDate < :from AND (subscription.endDate = "" OR subscription.endDate > :to))' + + ")", + { from: fromDate, to: toDate }, + ) + .andWhere("subscription.deleted != true") + .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS .getMany(); } async findNotYetCancelledSubscriptions(userId: string, date: string): Promise { - return (await this.getSubscriptionRepo()).createQueryBuilder('subscription') - .where('subscription.userId = :userId ', { userId: userId }) + return (await this.getSubscriptionRepo()) + .createQueryBuilder("subscription") + .where("subscription.userId = :userId ", { userId: userId }) .andWhere('(subscription.cancellationDate = "" OR subscription.cancellationDate > :date)', { date: date }) - .andWhere('subscription.deleted != true') - .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS + .andWhere("subscription.deleted != true") + .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS .getMany(); } async findSubscriptionForUserByPaymentRef(userId: string, paymentReference: string): Promise { - return (await this.getSubscriptionRepo()).createQueryBuilder('subscription') - .where('subscription.userId = :userId ', { userId: userId }) - .andWhere('subscription.paymentReference = :paymentReference', { paymentReference }) - .andWhere('subscription.deleted != true') - .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS - .orderBy('subscription.startDate', 'DESC') + return (await this.getSubscriptionRepo()) + .createQueryBuilder("subscription") + .where("subscription.userId = :userId ", { userId: userId }) + .andWhere("subscription.paymentReference = :paymentReference", { paymentReference }) + .andWhere("subscription.deleted != true") + .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS + .orderBy("subscription.startDate", "DESC") .getMany(); } async findSubscriptionsByTeamSubscriptionSlotId(teamSubscriptionSlotId: string): Promise { - return (await this.getSubscriptionRepo()).createQueryBuilder('subscription') - .where('subscription.teamSubscriptionSlotId = :teamSubscriptionSlotId', { teamSubscriptionSlotId: teamSubscriptionSlotId }) - .andWhere('subscription.deleted != true') - .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS - .orderBy('subscription.startDate', 'DESC') + return (await this.getSubscriptionRepo()) + .createQueryBuilder("subscription") + .where("subscription.teamSubscriptionSlotId = :teamSubscriptionSlotId", { + teamSubscriptionSlotId: teamSubscriptionSlotId, + }) + .andWhere("subscription.deleted != true") + .andWhere('subscription.planId != "free"') // TODO DEL FREE-SUBS + .orderBy("subscription.startDate", "DESC") .getMany(); } - async findActiveSubscriptionsByIdentity(authId: string[], authProvider: string): Promise<{ [authId:string]:SubscriptionAndUser[] }> { + async findActiveSubscriptionsByIdentity( + authId: string[], + authProvider: string, + ): Promise<{ [authId: string]: SubscriptionAndUser[] }> { const repo = await this.getSubscriptionRepo(); - const query = repo.createQueryBuilder('sub') - .innerJoinAndMapOne('sub.user', DBUser, 'user', 'sub.userId = user.id') - .innerJoinAndSelect('user.identities', 'ident', 'ident.authId = :authId AND ident.authProviderId = :authProvider', { authId, authProvider }) - .where('sub.startDate <= :date AND (:date < sub.endDate OR sub.endDate = "")', { date: new Date().toISOString() }) - .andWhere('sub.deleted != true') + const query = repo + .createQueryBuilder("sub") + .innerJoinAndMapOne("sub.user", DBUser, "user", "sub.userId = user.id") + .innerJoinAndSelect( + "user.identities", + "ident", + "ident.authId = :authId AND ident.authProviderId = :authProvider", + { authId, authProvider }, + ) + .where('sub.startDate <= :date AND (:date < sub.endDate OR sub.endDate = "")', { + date: new Date().toISOString(), + }) + .andWhere("sub.deleted != true") .andWhere('sub.planId != "free"') .orderBy("sub.startDate", "ASC"); const rows = await query.getMany(); - const result:{ [authId:string]:SubscriptionAndUser[] } = {}; + const result: { [authId: string]: SubscriptionAndUser[] } = {}; for (const r of rows) { const authId = ((r as any).user as User).identities[0].authId; - const subs = (result[authId] || []); + const subs = result[authId] || []; subs.push(r as SubscriptionAndUser); result[authId] = subs; } @@ -229,34 +272,45 @@ export class TypeORMAccountingDBImpl implements AccountingDB { async hadSubscriptionCreatedWithCoupon(userId: string, couponId: string): Promise { const repo = await this.getSubscriptionAdditionalDataRepo(); - const query = repo.createQueryBuilder('sad') - .select('1') - .leftJoinAndMapOne("sad.paymentReference", DBSubscription, "sub", "sub.paymentReference = sad.paymentReference") + const query = repo + .createQueryBuilder("sad") + .select("1") + .leftJoinAndMapOne( + "sad.paymentReference", + DBSubscription, + "sub", + "sub.paymentReference = sad.paymentReference", + ) .where("sub.userId = :userId", { userId }) // Either: // - it was created with that coupon // - or it still has that coupon applied. - .andWhere(`( + .andWhere( + `( JSON_SEARCH(JSON_EXTRACT(sad.coupons, '$[*].coupon_id'), 'one', :couponId) IS NOT NULL - )`, { couponId }); + )`, + { couponId }, + ); return (await query.getCount()) > 0; } async findSubscriptionAdditionalData(paymentReference: string): Promise { const repo = await this.getSubscriptionAdditionalDataRepo(); - return repo.createQueryBuilder('sad') + return repo + .createQueryBuilder("sad") .where("sad.paymentReference = :paymentReference", { paymentReference }) .getOne(); } - async storeSubscriptionAdditionalData(subscriptionData: DBSubscriptionAdditionalData): Promise { + async storeSubscriptionAdditionalData( + subscriptionData: DBSubscriptionAdditionalData, + ): Promise { const repo = await this.getSubscriptionAdditionalDataRepo(); return repo.save(subscriptionData); } } export class TransactionalAccountingDBImpl extends TypeORMAccountingDBImpl { - constructor(protected readonly manager: EntityManager) { super(); } @@ -264,4 +318,4 @@ export class TransactionalAccountingDBImpl extends TypeORMAccountingDBImpl { async getEntityManager(): Promise { return this.manager; } -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/app-installation-db-impl.ts b/components/gitpod-db/src/typeorm/app-installation-db-impl.ts index 9c10997f1b91f9..0459dca200a065 100644 --- a/components/gitpod-db/src/typeorm/app-installation-db-impl.ts +++ b/components/gitpod-db/src/typeorm/app-installation-db-impl.ts @@ -15,7 +15,6 @@ import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; @injectable() export class TypeORMAppInstallationDBImpl implements AppInstallationDB { - @inject(TypeORM) typeORM: TypeORM; protected async getEntityManager() { @@ -26,7 +25,13 @@ export class TypeORMAppInstallationDBImpl implements AppInstallationDB { return (await this.getEntityManager()).getRepository(DBAppInstallation); } - public async recordNewInstallation(platform: AppInstallationPlatform, source: 'user' | 'platform', installationID: string, ownerUserID?: string, platformUserID?: string): Promise { + public async recordNewInstallation( + platform: AppInstallationPlatform, + source: "user" | "platform", + installationID: string, + ownerUserID?: string, + platformUserID?: string, + ): Promise { const repo = await this.getRepo(); const obj = new DBAppInstallation(); @@ -39,9 +44,13 @@ export class TypeORMAppInstallationDBImpl implements AppInstallationDB { await repo.insert(obj); } - public async findInstallation(platform: AppInstallationPlatform, installationID: string): Promise { + public async findInstallation( + platform: AppInstallationPlatform, + installationID: string, + ): Promise { const repo = await this.getRepo(); - const qb = repo.createQueryBuilder('installation') + const qb = repo + .createQueryBuilder("installation") .where("installation.installationID = :installationID", { installationID }) .andWhere('installation.state != "uninstalled"') .orderBy("installation.lastUpdateTime", "DESC") @@ -50,18 +59,20 @@ export class TypeORMAppInstallationDBImpl implements AppInstallationDB { return (await qb.getMany())[0]; } - public async recordUninstallation(platform: AppInstallationPlatform, source: 'user' | 'platform', installationID: string) { + public async recordUninstallation( + platform: AppInstallationPlatform, + source: "user" | "platform", + installationID: string, + ) { const installation = await this.findInstallation(platform, installationID); if (!installation) { log.warn("Cannot record uninstallation of non-existent installation", { platform, installationID }); return; } - installation.state = 'uninstalled'; + installation.state = "uninstalled"; const repo = await this.getRepo(); await repo.save(installation); } - - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/auth-code-repository-db.ts b/components/gitpod-db/src/typeorm/auth-code-repository-db.ts index 7d2336d0a3246b..f4c9444301b7ff 100644 --- a/components/gitpod-db/src/typeorm/auth-code-repository-db.ts +++ b/components/gitpod-db/src/typeorm/auth-code-repository-db.ts @@ -4,18 +4,24 @@ * See License-AGPL.txt in the project root for license information. */ -import { DateInterval, OAuthAuthCode, OAuthAuthCodeRepository, OAuthClient, OAuthScope, OAuthUser } from "@jmondi/oauth2-server"; -import * as crypto from 'crypto'; +import { + DateInterval, + OAuthAuthCode, + OAuthAuthCodeRepository, + OAuthClient, + OAuthScope, + OAuthUser, +} from "@jmondi/oauth2-server"; +import * as crypto from "crypto"; import { inject, injectable } from "inversify"; import { EntityManager, Repository } from "typeorm"; -import { DBOAuthAuthCodeEntry } from './entity/db-oauth-auth-code'; -import { TypeORM } from './typeorm'; +import { DBOAuthAuthCodeEntry } from "./entity/db-oauth-auth-code"; +import { TypeORM } from "./typeorm"; const expiryInFuture = new DateInterval("5m"); @injectable() export class AuthCodeRepositoryDB implements OAuthAuthCodeRepository { - @inject(TypeORM) private readonly typeORM: TypeORM; @@ -30,7 +36,7 @@ export class AuthCodeRepositoryDB implements OAuthAuthCodeRepository { public async getByIdentifier(authCodeCode: string): Promise { const authCodeRepo = await this.getOauthAuthCodeRepo(); let authCodes = await authCodeRepo.find({ code: authCodeCode }); - authCodes = authCodes.filter(te => (new Date(te.expiresAt)).getTime() > Date.now()); + authCodes = authCodes.filter((te) => new Date(te.expiresAt).getTime() > Date.now()); const authCode = authCodes.length > 0 ? authCodes[0] : undefined; if (!authCode) { throw new Error(`authentication code not found`); @@ -38,7 +44,7 @@ export class AuthCodeRepositoryDB implements OAuthAuthCodeRepository { return authCode; } public issueAuthCode(client: OAuthClient, user: OAuthUser | undefined, scopes: OAuthScope[]): OAuthAuthCode { - const code = crypto.randomBytes(30).toString('hex'); + const code = crypto.randomBytes(30).toString("hex"); // NOTE: caller (@jmondi/oauth2-server) is responsible for adding the remaining items, PKCE params, redirect URL, etc return { code: code, diff --git a/components/gitpod-db/src/typeorm/auth-provider-entry-db-impl.ts b/components/gitpod-db/src/typeorm/auth-provider-entry-db-impl.ts index c043c5da1930f2..e4edaa68cc55d2 100644 --- a/components/gitpod-db/src/typeorm/auth-provider-entry-db-impl.ts +++ b/components/gitpod-db/src/typeorm/auth-provider-entry-db-impl.ts @@ -15,7 +15,6 @@ import { createHash } from "crypto"; @injectable() export class AuthProviderEntryDBImpl implements AuthProviderEntryDB { - @inject(TypeORM) typeORM: TypeORM; protected async getEntityManager(): Promise { @@ -40,9 +39,12 @@ export class AuthProviderEntryDBImpl implements AuthProviderEntryDB { async delete({ id }: AuthProviderEntry): Promise { // 1. virtually unlink identities using this provider from all users const identitiesRepo = await this.getIdentitiesRepo(); - await identitiesRepo.query(`UPDATE d_b_identity AS i + await identitiesRepo.query( + `UPDATE d_b_identity AS i SET i.deleted = TRUE - WHERE i.authProviderId = ?;`, [ id ]); + WHERE i.authProviderId = ?;`, + [id], + ); // 2. then mark as deleted const repo = await this.getAuthProviderRepo(); @@ -50,13 +52,14 @@ export class AuthProviderEntryDBImpl implements AuthProviderEntryDB { } async findAll(exceptOAuthRevisions: string[] = []): Promise { - exceptOAuthRevisions = exceptOAuthRevisions.filter(r => r !== ""); // never filter out '' which means "undefined" in the DB + exceptOAuthRevisions = exceptOAuthRevisions.filter((r) => r !== ""); // never filter out '' which means "undefined" in the DB const repo = await this.getAuthProviderRepo(); - let query = repo.createQueryBuilder('auth_provider') - .where('auth_provider.deleted != true'); + let query = repo.createQueryBuilder("auth_provider").where("auth_provider.deleted != true"); if (exceptOAuthRevisions.length > 0) { - query = query.andWhere('auth_provider.oauthRevision NOT IN (:...exceptOAuthRevisions)', { exceptOAuthRevisions }); + query = query.andWhere("auth_provider.oauthRevision NOT IN (:...exceptOAuthRevisions)", { + exceptOAuthRevisions, + }); } return query.getMany(); } @@ -65,31 +68,31 @@ export class AuthProviderEntryDBImpl implements AuthProviderEntryDB { const hostField: keyof DBAuthProviderEntry = "host"; const repo = await this.getAuthProviderRepo(); - const query = repo.createQueryBuilder('auth_provider') - .select(hostField) - .where('auth_provider.deleted != true'); + const query = repo.createQueryBuilder("auth_provider").select(hostField).where("auth_provider.deleted != true"); const result = (await query.execute()) as Pick[]; - return result.map(r => r.host); + return result.map((r) => r.host); } async findByHost(host: string): Promise { const repo = await this.getAuthProviderRepo(); - const query = repo.createQueryBuilder('auth_provider') + const query = repo + .createQueryBuilder("auth_provider") .where(`auth_provider.host = :host`, { host }) - .andWhere('auth_provider.deleted != true'); + .andWhere("auth_provider.deleted != true"); return query.getOne(); } async findByUserId(ownerId: string): Promise { const repo = await this.getAuthProviderRepo(); - const query = repo.createQueryBuilder('auth_provider') + const query = repo + .createQueryBuilder("auth_provider") .where(`auth_provider.ownerId = :ownerId`, { ownerId }) - .andWhere('auth_provider.deleted != true'); + .andWhere("auth_provider.deleted != true"); return query.getMany(); } protected oauthContentHash(oauth: AuthProviderEntry["oauth"]): string { - const result = createHash('sha256').update(JSON.stringify(oauth)).digest('hex'); + const result = createHash("sha256").update(JSON.stringify(oauth)).digest("hex"); return result; } } diff --git a/components/gitpod-db/src/typeorm/code-sync-resource-db.spec.db.ts b/components/gitpod-db/src/typeorm/code-sync-resource-db.spec.db.ts index 7303844176396f..2826665fa890ad 100644 --- a/components/gitpod-db/src/typeorm/code-sync-resource-db.spec.db.ts +++ b/components/gitpod-db/src/typeorm/code-sync-resource-db.spec.db.ts @@ -4,18 +4,16 @@ * See License-AGPL.txt in the project root for license information. */ -import uuid = require('uuid'); -import * as chai from 'chai'; -import { suite, test, timeout } from 'mocha-typescript'; -import { testContainer } from '../test-container'; -import { CodeSyncResourceDB } from './code-sync-resource-db'; -import { IUserDataManifest, SyncResource } from './entity/db-code-sync-resource'; +import uuid = require("uuid"); +import * as chai from "chai"; +import { suite, test, timeout } from "mocha-typescript"; +import { testContainer } from "../test-container"; +import { CodeSyncResourceDB } from "./code-sync-resource-db"; +import { IUserDataManifest, SyncResource } from "./entity/db-code-sync-resource"; const expect = chai.expect; - @suite(timeout(10000)) export class CodeSyncResourceDBSpec { - private readonly db = testContainer.get(CodeSyncResourceDB); private userId: string; @@ -33,51 +31,51 @@ export class CodeSyncResourceDBSpec { const doInsert = async () => { inserted = true; }; - const kind = 'machines'; - let latest = await this.db.getResource(this.userId, kind, 'latest'); + const kind = "machines"; + let latest = await this.db.getResource(this.userId, kind, "latest"); expect(latest).to.be.undefined; - let inserted = false + let inserted = false; let rev = await this.db.insert(this.userId, kind, doInsert); expect(rev).not.to.be.undefined; expect(inserted).to.be.true; - latest = await this.db.getResource(this.userId, kind, 'latest') + latest = await this.db.getResource(this.userId, kind, "latest"); expect(latest?.rev).to.deep.equal(rev); const resource = await this.db.getResource(this.userId, kind, rev!); expect(resource).to.deep.equal(latest); - inserted = false + inserted = false; rev = await this.db.insert(this.userId, kind, doInsert, { - latestRev: uuid.v4() + latestRev: uuid.v4(), }); expect(rev).to.be.undefined; expect(inserted).to.be.false; - inserted = false + inserted = false; rev = await this.db.insert(this.userId, kind, doInsert, { - latestRev: latest?.rev + latestRev: latest?.rev, }); expect(rev).not.to.be.undefined; - expect(rev).not.to.eq(latest?.rev) + expect(rev).not.to.eq(latest?.rev); expect(inserted).to.be.true; } @test() async getResources(): Promise { - const kind = 'machines'; + const kind = "machines"; let resources = await this.db.getResources(this.userId, kind); expect(resources).to.be.empty; const expected = []; for (let i = 0; i < 5; i++) { - const rev = await this.db.insert(this.userId, kind, async () => { }); + const rev = await this.db.insert(this.userId, kind, async () => {}); expected.unshift(rev); } resources = await this.db.getResources(this.userId, kind); - expect(resources.map(r => r.rev)).to.deep.equal(expected); + expect(resources.map((r) => r.rev)).to.deep.equal(expected); } @test() @@ -88,52 +86,51 @@ export class CodeSyncResourceDBSpec { latest: {}, }); - let machinesRev = await this.db.insert(this.userId, 'machines', async () => { }); + let machinesRev = await this.db.insert(this.userId, "machines", async () => {}); manifest = await this.db.getManifest(this.userId); expect(manifest).to.deep.eq({ session: this.userId, latest: { - machines: machinesRev + machines: machinesRev, }, }); - let extensionsRev = await this.db.insert(this.userId, SyncResource.Extensions, async () => { }); + let extensionsRev = await this.db.insert(this.userId, SyncResource.Extensions, async () => {}); manifest = await this.db.getManifest(this.userId); expect(manifest).to.deep.eq({ session: this.userId, latest: { machines: machinesRev, - extensions: extensionsRev + extensions: extensionsRev, }, }); - machinesRev = await this.db.insert(this.userId, 'machines', async () => { }); + machinesRev = await this.db.insert(this.userId, "machines", async () => {}); manifest = await this.db.getManifest(this.userId); expect(manifest).to.deep.eq({ session: this.userId, latest: { machines: machinesRev, - extensions: extensionsRev + extensions: extensionsRev, }, }); } @test() async roundRobinInsert(): Promise { - const kind = 'machines'; + const kind = "machines"; const expectation: string[] = []; const doInsert = async (rev: string, oldRevs: string[]) => { for (let rev of oldRevs) { await this.db.deleteResource(this.userId, kind, rev, async () => {}); } - } + }; const revLimit = 3; const assertResources = async () => { - const resources = await this.db.getResources(this.userId, kind); - expect(resources.map(r => r.rev)).to.deep.eq(expectation); - } + expect(resources.map((r) => r.rev)).to.deep.eq(expectation); + }; await assertResources(); @@ -157,5 +154,4 @@ export class CodeSyncResourceDBSpec { expectation.length = revLimit; await assertResources(); } - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/code-sync-resource-db.ts b/components/gitpod-db/src/typeorm/code-sync-resource-db.ts index c4a5c5916b5f04..7ded3f537d5d8d 100644 --- a/components/gitpod-db/src/typeorm/code-sync-resource-db.ts +++ b/components/gitpod-db/src/typeorm/code-sync-resource-db.ts @@ -4,39 +4,39 @@ * See License-AGPL.txt in the project root for license information. */ -import { inject, injectable } from 'inversify'; -import { EntityManager } from 'typeorm'; -import uuid = require('uuid'); -import { DBCodeSyncResource, IUserDataManifest, ServerResource } from './entity/db-code-sync-resource'; -import { TypeORM } from './typeorm'; +import { inject, injectable } from "inversify"; +import { EntityManager } from "typeorm"; +import uuid = require("uuid"); +import { DBCodeSyncResource, IUserDataManifest, ServerResource } from "./entity/db-code-sync-resource"; +import { TypeORM } from "./typeorm"; export interface CodeSyncInsertOptions { - latestRev?: string - revLimit?: number + latestRev?: string; + revLimit?: number; } @injectable() export class CodeSyncResourceDB { - @inject(TypeORM) private readonly typeORM: TypeORM; async getManifest(userId: string): Promise { const connection = await this.typeORM.getConnection(); const resources = await connection.manager - .createQueryBuilder(DBCodeSyncResource, 'resource') - .where('resource.userId = :userId AND resource.deleted = 0', { userId }) - .andWhere(qb => { - const subQuery = qb.subQuery() - .select('resource2.userId') - .addSelect('resource2.kind') - .addSelect('max(resource2.created)') - .from(DBCodeSyncResource, 'resource2') - .where('resource2.userId = :userId AND resource2.deleted = 0', { userId }) - .groupBy('resource2.kind') - .orderBy('resource2.created', 'DESC') + .createQueryBuilder(DBCodeSyncResource, "resource") + .where("resource.userId = :userId AND resource.deleted = 0", { userId }) + .andWhere((qb) => { + const subQuery = qb + .subQuery() + .select("resource2.userId") + .addSelect("resource2.kind") + .addSelect("max(resource2.created)") + .from(DBCodeSyncResource, "resource2") + .where("resource2.userId = :userId AND resource2.deleted = 0", { userId }) + .groupBy("resource2.kind") + .orderBy("resource2.created", "DESC") .getQuery(); - return '(resource.userId,resource.kind,resource.created) IN ' + subQuery; + return "(resource.userId,resource.kind,resource.created) IN " + subQuery; }) .getMany(); @@ -60,8 +60,9 @@ export class CodeSyncResourceDB { async delete(userId: string, doDelete: () => Promise): Promise { const connection = await this.typeORM.getConnection(); - await connection.transaction(async manager => { - await manager.createQueryBuilder() + await connection.transaction(async (manager) => { + await manager + .createQueryBuilder() .update(DBCodeSyncResource) .set({ deleted: true }) .where("userId = :userId AND deleted = 0", { userId }) @@ -70,10 +71,16 @@ export class CodeSyncResourceDB { }); } - async deleteResource(userId: string, kind: ServerResource, rev: string, doDelete: (rev: string) => Promise): Promise { + async deleteResource( + userId: string, + kind: ServerResource, + rev: string, + doDelete: (rev: string) => Promise, + ): Promise { const connection = await this.typeORM.getConnection(); - await connection.transaction(async manager => { - await manager.createQueryBuilder() + await connection.transaction(async (manager) => { + await manager + .createQueryBuilder() .delete() .from(DBCodeSyncResource) .where("userId = :userId AND kind = :kind AND rev = :rev", { userId, kind, rev: rev }) @@ -82,9 +89,14 @@ export class CodeSyncResourceDB { }); } - async insert(userId: string, kind: ServerResource, doInsert: (rev: string, oldRevs: string[]) => Promise, params?: CodeSyncInsertOptions): Promise { + async insert( + userId: string, + kind: ServerResource, + doInsert: (rev: string, oldRevs: string[]) => Promise, + params?: CodeSyncInsertOptions, + ): Promise { const connection = await this.typeORM.getConnection(); - return await connection.transaction(async manager => { + return await connection.transaction(async (manager) => { let latest: DBCodeSyncResource | undefined; let toDeleted: DBCodeSyncResource[] = []; if (params?.revLimit) { @@ -95,41 +107,54 @@ export class CodeSyncResourceDB { toDeleted = resources.splice(params?.revLimit - 1); } } else { - latest = await this.doGetResource(manager, userId, kind, 'latest'); + latest = await this.doGetResource(manager, userId, kind, "latest"); } // user setting always show with diff so we need to make sure it’s changed from prev revision if (params?.latestRev && latest?.rev !== params.latestRev) { return undefined; } const rev = uuid.v4(); - await manager.createQueryBuilder() + await manager + .createQueryBuilder() .insert() .into(DBCodeSyncResource) .values({ userId, kind, rev }) .execute(); - await doInsert(rev, toDeleted.map(e => e.rev)); + await doInsert( + rev, + toDeleted.map((e) => e.rev), + ); return rev; }); } - private doGetResource(manager: EntityManager, userId: string, kind: ServerResource, rev: string | 'latest'): Promise { + private doGetResource( + manager: EntityManager, + userId: string, + kind: ServerResource, + rev: string | "latest", + ): Promise { let qb = manager - .createQueryBuilder(DBCodeSyncResource, 'resource') - .where('resource.userId = :userId AND resource.kind = :kind AND resource.deleted = 0', { userId, kind }); - if (rev === 'latest') { - qb.orderBy('resource.created', 'DESC'); + .createQueryBuilder(DBCodeSyncResource, "resource") + .where("resource.userId = :userId AND resource.kind = :kind AND resource.deleted = 0", { userId, kind }); + if (rev === "latest") { + qb.orderBy("resource.created", "DESC"); } else { - qb = qb.andWhere('resource.rev = :rev', { rev }); + qb = qb.andWhere("resource.rev = :rev", { rev }); } return qb.getOne(); } - private doGetResources(manager: EntityManager, userId: string, kind: ServerResource): Promise { - return manager.getRepository(DBCodeSyncResource) - .createQueryBuilder('resource') - .where('resource.userId = :userId AND resource.kind = :kind AND resource.deleted = 0', { userId, kind }) - .orderBy('resource.created', 'DESC') + private doGetResources( + manager: EntityManager, + userId: string, + kind: ServerResource, + ): Promise { + return manager + .getRepository(DBCodeSyncResource) + .createQueryBuilder("resource") + .where("resource.userId = :userId AND resource.kind = :kind AND resource.deleted = 0", { userId, kind }) + .orderBy("resource.created", "DESC") .getMany(); } - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/deleted-entry-gc.ts b/components/gitpod-db/src/typeorm/deleted-entry-gc.ts index 86ebfdd2b43bf1..e7b6225dfbd7e0 100644 --- a/components/gitpod-db/src/typeorm/deleted-entry-gc.ts +++ b/components/gitpod-db/src/typeorm/deleted-entry-gc.ts @@ -10,7 +10,6 @@ import { Config } from "../config"; import { repeat } from "@gitpod/gitpod-protocol/lib/util/repeat"; import { Disposable, DisposableCollection } from "@gitpod/gitpod-protocol"; - @injectable() export class DeletedEntryGC implements Disposable { @inject(TypeORM) protected readonly typeORM: TypeORM; @@ -21,13 +20,16 @@ export class DeletedEntryGC implements Disposable { public start() { const cfg = this.config.deletedEntryGCConfig; if (!cfg.enabled) { - console.info("Deleted Entries GC disabled") + console.info("Deleted Entries GC disabled"); return; } - console.info(`Deleted Entries GC enabled (running every ${cfg.intervalMS/(60*1000)} minutes)`); + console.info(`Deleted Entries GC enabled (running every ${cfg.intervalMS / (60 * 1000)} minutes)`); this.disposables.push( - repeat(() => this.gc().catch(e => console.error("error while removing deleted entries", e)), cfg.intervalMS) + repeat( + () => this.gc().catch((e) => console.error("error while removing deleted entries", e)), + cfg.intervalMS, + ), ); } @@ -37,9 +39,8 @@ export class DeletedEntryGC implements Disposable { protected async gc() { const conn = await this.typeORM.getConnection(); - await Promise.all(tables.map(t => conn.query(`DELETE FROM ${t.name} WHERE ${t.deletionColumn} = 1`))); + await Promise.all(tables.map((t) => conn.query(`DELETE FROM ${t.name} WHERE ${t.deletionColumn} = 1`))); } - } const tables: TableWithDeletion[] = [ @@ -66,4 +67,3 @@ interface TableWithDeletion { name: string; deletionColumn: string; } - diff --git a/components/gitpod-db/src/typeorm/edu-email-domain-db-impl.ts b/components/gitpod-db/src/typeorm/edu-email-domain-db-impl.ts index 3ccbe7624d421c..29ad2fe2f21bb8 100644 --- a/components/gitpod-db/src/typeorm/edu-email-domain-db-impl.ts +++ b/components/gitpod-db/src/typeorm/edu-email-domain-db-impl.ts @@ -14,7 +14,6 @@ import { DBEduEmailDomain } from "./entity/db-edu-email-domain"; @injectable() export class EduEmailDomainDBImpl implements EduEmailDomainDB { - @inject(TypeORM) typeorm: TypeORM; protected async getManager(): Promise { @@ -32,8 +31,7 @@ export class EduEmailDomainDBImpl implements EduEmailDomainDB { async readEducationalInstitutionDomains(): Promise { const repo = await this.getRepo(); - const result = await repo.createQueryBuilder("entry") - .getMany(); + const result = await repo.createQueryBuilder("entry").getMany(); return result; } -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/email-db-impl.ts b/components/gitpod-db/src/typeorm/email-db-impl.ts index ced97279509c8c..d185ce92da3672 100644 --- a/components/gitpod-db/src/typeorm/email-db-impl.ts +++ b/components/gitpod-db/src/typeorm/email-db-impl.ts @@ -37,7 +37,8 @@ export class TypeORMEMailDBImpl implements EMailDB { async findEMailsToSend(limit: number): Promise { const repo = await this.getEMailRepo(); - const query = repo.createQueryBuilder('email') + const query = repo + .createQueryBuilder("email") .where("email.scheduledSendgridTime = ''") .orderBy("email.scheduledInternalTime") .limit(limit); @@ -47,9 +48,10 @@ export class TypeORMEMailDBImpl implements EMailDB { async findEMailsByCampaignAndUserId(campaignId: string, userId: string): Promise { const repo = await this.getEMailRepo(); - const qb = repo.createQueryBuilder('email') + const qb = repo + .createQueryBuilder("email") .where("email.campaignId = :campaignId", { campaignId }) .andWhere("email.userId = :userId", { userId }); return qb.getMany(); } -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/email-domain-filter-db-impl.ts b/components/gitpod-db/src/typeorm/email-domain-filter-db-impl.ts index 2326ebadd12be6..b5f33c926f3f4c 100644 --- a/components/gitpod-db/src/typeorm/email-domain-filter-db-impl.ts +++ b/components/gitpod-db/src/typeorm/email-domain-filter-db-impl.ts @@ -14,7 +14,6 @@ import { DBEmailDomainFilterEntry } from "./entity/db-email-domain-filter-entry" @injectable() export class EmailDomainFilterDBImpl implements EmailDomainFilterDB { - @inject(TypeORM) typeorm: TypeORM; protected async getManager(): Promise { @@ -32,10 +31,11 @@ export class EmailDomainFilterDBImpl implements EmailDomainFilterDB { async filter(domain: string): Promise { const repo = await this.getRepo(); - const result = await repo.createQueryBuilder("entry") + const result = await repo + .createQueryBuilder("entry") .where(`entry.domain = :domain`, { domain: domain }) .andWhere(`entry.negative = '1'`) .getOne(); return !result; } -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-account-entry.ts b/components/gitpod-db/src/typeorm/entity/db-account-entry.ts index 42e5eda9305bc1..6b2a0a162fa490 100644 --- a/components/gitpod-db/src/typeorm/entity/db-account-entry.ts +++ b/components/gitpod-db/src/typeorm/entity/db-account-entry.ts @@ -13,41 +13,40 @@ import { Transformer } from "../../typeorm/transformer"; @Entity() // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBAccountEntry implements AccountEntry { - @PrimaryColumn("uuid") uid: string; @Column(TypeORM.UUID_COLUMN_TYPE) userId: string; - @Column('double') + @Column("double") amount: number; @Column() date: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) expiryDate?: string; @Column({ - type: 'char', - length: 7 + type: "char", + length: 7, }) - kind: AccountEntryKind + kind: AccountEntryKind; @Column({ - type: 'simple-json', - nullable: true + type: "simple-json", + nullable: true, }) description?: object; @Column({ - type: 'char', + type: "char", length: 36, - nullable: true + nullable: true, }) creditId?: string; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-app-installation.ts b/components/gitpod-db/src/typeorm/entity/db-app-installation.ts index 7475b22ac27b50..ea52ed567d4d03 100644 --- a/components/gitpod-db/src/typeorm/entity/db-app-installation.ts +++ b/components/gitpod-db/src/typeorm/entity/db-app-installation.ts @@ -10,7 +10,7 @@ import { TypeORM } from "../typeorm"; import { Transformer } from "../transformer"; @Entity() -@Index("ind_dbsync", ["creationTime"]) // DBSync +@Index("ind_dbsync", ["creationTime"]) // DBSync export class DBAppInstallation implements AppInstallation { @PrimaryColumn() platform: AppInstallationPlatform; @@ -20,7 +20,7 @@ export class DBAppInstallation implements AppInstallation { @Column({ ...TypeORM.UUID_COLUMN_TYPE, - nullable: true + nullable: true, }) ownerUserID?: string; @@ -31,19 +31,18 @@ export class DBAppInstallation implements AppInstallation { state: AppInstallationState; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - default: () => 'CURRENT_TIMESTAMP(6)', - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + default: () => "CURRENT_TIMESTAMP(6)", + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) creationTime: string; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - default: () => 'CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)', - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + default: () => "CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)", + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) lastUpdateTime: string; - } diff --git a/components/gitpod-db/src/typeorm/entity/db-auth-provider-entry.ts b/components/gitpod-db/src/typeorm/entity/db-auth-provider-entry.ts index 43f20b84aaffa4..46da1294406410 100644 --- a/components/gitpod-db/src/typeorm/entity/db-auth-provider-entry.ts +++ b/components/gitpod-db/src/typeorm/entity/db-auth-provider-entry.ts @@ -18,13 +18,13 @@ export class DBAuthProviderEntry implements AuthProviderEntry { @Column() ownerId: string; - @Column('varchar') + @Column("varchar") status: AuthProviderEntry.Status; @Column() host: string; - @Column('varchar') + @Column("varchar") type: AuthProviderEntry.Type; @Column({ @@ -32,14 +32,14 @@ export class DBAuthProviderEntry implements AuthProviderEntry { transformer: Transformer.compose( Transformer.SIMPLE_JSON([]), // Relies on the initialization of the var in UserDbImpl - Transformer.encrypted(() => encryptionService) - ) + Transformer.encrypted(() => encryptionService), + ), }) oauth: OAuth2Config; @Index("ind_oauthRevision") @Column({ - default: '', + default: "", transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) oauthRevision?: string; diff --git a/components/gitpod-db/src/typeorm/entity/db-code-sync-resource.ts b/components/gitpod-db/src/typeorm/entity/db-code-sync-resource.ts index 797660e9039032..d8269b5f818025 100644 --- a/components/gitpod-db/src/typeorm/entity/db-code-sync-resource.ts +++ b/components/gitpod-db/src/typeorm/entity/db-code-sync-resource.ts @@ -14,30 +14,37 @@ export interface IUserData { } export const enum SyncResource { - Settings = 'settings', - Keybindings = 'keybindings', - Snippets = 'snippets', - Tasks = 'tasks', - Extensions = 'extensions', - GlobalState = 'globalState', + Settings = "settings", + Keybindings = "keybindings", + Snippets = "snippets", + Tasks = "tasks", + Extensions = "extensions", + GlobalState = "globalState", } -export const ALL_SYNC_RESOURCES: SyncResource[] = [SyncResource.Settings, SyncResource.Keybindings, SyncResource.Snippets, SyncResource.Tasks, SyncResource.Extensions, SyncResource.GlobalState]; +export const ALL_SYNC_RESOURCES: SyncResource[] = [ + SyncResource.Settings, + SyncResource.Keybindings, + SyncResource.Snippets, + SyncResource.Tasks, + SyncResource.Extensions, + SyncResource.GlobalState, +]; export interface IUserDataManifest { - readonly latest?: Record; - readonly session: string; + readonly latest?: Record; + readonly session: string; /** * This property reflects a weak ETag for caching code sync responses, * in the server, this is send in the Etag header and it's calculated by Express.js or we can override it manually. */ - //readonly ref: string; + //readonly ref: string; } -export type ServerResource = SyncResource | 'machines'; -export const ALL_SERVER_RESOURCES: ServerResource[] = [...ALL_SYNC_RESOURCES, 'machines']; +export type ServerResource = SyncResource | "machines"; +export const ALL_SERVER_RESOURCES: ServerResource[] = [...ALL_SYNC_RESOURCES, "machines"]; @Entity() -@Index('ind_dbsync', ['created']) // DBSync +@Index("ind_dbsync", ["created"]) // DBSync export class DBCodeSyncResource { @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) userId: string; @@ -49,7 +56,7 @@ export class DBCodeSyncResource { rev: string; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, // a custom, since Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP rounds to seconds in `from` transformer: { @@ -60,8 +67,8 @@ export class DBCodeSyncResource { from(value: any): any { // From TIMESTAMP to ISO string return new Date(value).toISOString(); - } - } + }, + }, }) created: string; diff --git a/components/gitpod-db/src/typeorm/entity/db-email.ts b/components/gitpod-db/src/typeorm/entity/db-email.ts index 83a35d3f4b95df..58271571534960 100644 --- a/components/gitpod-db/src/typeorm/entity/db-email.ts +++ b/components/gitpod-db/src/typeorm/entity/db-email.ts @@ -9,16 +9,15 @@ import { EMail, EMailParameters } from "@gitpod/gitpod-protocol"; import { TypeORM } from "../../typeorm/typeorm"; import { Transformer } from "../../typeorm/transformer"; -@Entity('d_b_email') +@Entity("d_b_email") // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync @Index("ind_campaignId_userId", ["campaignId", "userId"]) export class DBEmail implements EMail { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) uid: string; @Column({ - length: 30 + length: 30, }) campaignId: string; @@ -28,21 +27,21 @@ export class DBEmail implements EMail { @Column() recipientAddress: string; - @Column('json') + @Column("json") params: EMailParameters; @Column() scheduledInternalTime: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) scheduledSendgridTime?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) error?: string; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-gitpod-token.ts b/components/gitpod-db/src/typeorm/entity/db-gitpod-token.ts index ccd2ffc1f84e1a..d3f2367d6cd47e 100644 --- a/components/gitpod-db/src/typeorm/entity/db-gitpod-token.ts +++ b/components/gitpod-db/src/typeorm/entity/db-gitpod-token.ts @@ -4,37 +4,36 @@ * See License-AGPL.txt in the project root for license information. */ -import { GitpodToken, GitpodTokenType } from "@gitpod/gitpod-protocol" -import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm" -import { Transformer } from "../transformer" -import { DBUser } from "./db-user" +import { GitpodToken, GitpodTokenType } from "@gitpod/gitpod-protocol"; +import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm"; +import { Transformer } from "../transformer"; +import { DBUser } from "./db-user"; @Entity() // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBGitpodToken implements GitpodToken { - - @PrimaryColumn('varchar') - tokenHash: string + @PrimaryColumn("varchar") + tokenHash: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) - name?: string + name?: string; - @Column({ type: 'int' }) - type: GitpodTokenType + @Column({ type: "int" }) + type: GitpodTokenType; - @ManyToOne(type => DBUser) + @ManyToOne((type) => DBUser) @JoinColumn() - user: DBUser + user: DBUser; @Column("simple-array") - scopes: string[] + scopes: string[]; @Column() - created: string + created: string; @Column() deleted?: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-identity.ts b/components/gitpod-db/src/typeorm/entity/db-identity.ts index d962274ee5ec7d..30098610f97c23 100644 --- a/components/gitpod-db/src/typeorm/entity/db-identity.ts +++ b/components/gitpod-db/src/typeorm/entity/db-identity.ts @@ -11,25 +11,25 @@ import { DBUser } from "./db-user"; import { Transformer } from "../transformer"; @Entity() -@Index("ind_authProviderId_authName", ['authProviderId', 'authName']) +@Index("ind_authProviderId_authName", ["authProviderId", "authName"]) // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBIdentity implements Identity { - @PrimaryColumn('varchar') + @PrimaryColumn("varchar") authProviderId: string; - @PrimaryColumn('varchar') + @PrimaryColumn("varchar") authId: string; /** Workaround: Typeorm does not (yet) support uni-directional OneToMany relations */ - @ManyToOne(type => DBUser, user => user.identities) + @ManyToOne((type) => DBUser, (user) => user.identities) user: DBUser; @Column() authName: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) primaryEmail?: string; @@ -43,8 +43,8 @@ export class DBIdentity implements Identity { }, from(value: any): any { return []; - } - } + }, + }, }) tokens: Token[]; @@ -53,4 +53,4 @@ export class DBIdentity implements Identity { @Column() readonly?: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-installation-admin.ts b/components/gitpod-db/src/typeorm/entity/db-installation-admin.ts index 2b01c814b41c13..87300911dd06fb 100644 --- a/components/gitpod-db/src/typeorm/entity/db-installation-admin.ts +++ b/components/gitpod-db/src/typeorm/entity/db-installation-admin.ts @@ -10,9 +10,9 @@ import { TypeORM } from "../typeorm"; @Entity() export class DBInstallationAdmin implements InstallationAdmin { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) - id: string; + @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) + id: string; - @Column('simple-json') - settings: InstallationAdminSettings; + @Column("simple-json") + settings: InstallationAdminSettings; } diff --git a/components/gitpod-db/src/typeorm/entity/db-layout-data.ts b/components/gitpod-db/src/typeorm/entity/db-layout-data.ts index d81a8944a0d08d..f8a5d4539f4b3f 100644 --- a/components/gitpod-db/src/typeorm/entity/db-layout-data.ts +++ b/components/gitpod-db/src/typeorm/entity/db-layout-data.ts @@ -12,19 +12,17 @@ import { TypeORM } from "../typeorm"; @Entity() export class DBLayoutData implements LayoutData { - @PrimaryColumn(TypeORM.WORKSPACE_ID_COLUMN_TYPE) workspaceId: string; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - default: () => 'CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)', - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + default: () => "CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)", + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) lastUpdatedTime: string; @Column() layoutData: string; - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-license-key.ts b/components/gitpod-db/src/typeorm/entity/db-license-key.ts index c6e298f9172af8..b69ee0b08afc82 100644 --- a/components/gitpod-db/src/typeorm/entity/db-license-key.ts +++ b/components/gitpod-db/src/typeorm/entity/db-license-key.ts @@ -11,19 +11,17 @@ import { Transformer } from "../transformer"; @Entity() export class DBLicenseKey { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) id: string; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - default: () => 'CURRENT_TIMESTAMP(6)', - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + default: () => "CURRENT_TIMESTAMP(6)", + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) installationTime: string; @Column() key: string; - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-oauth-auth-code.ts b/components/gitpod-db/src/typeorm/entity/db-oauth-auth-code.ts index 2b91d35d1006a2..0f31d27d2dac17 100644 --- a/components/gitpod-db/src/typeorm/entity/db-oauth-auth-code.ts +++ b/components/gitpod-db/src/typeorm/entity/db-oauth-auth-code.ts @@ -9,7 +9,7 @@ import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from "t import { Transformer } from "../transformer"; import { DBUser } from "./db-user"; -@Entity({ name: 'd_b_oauth_auth_code_entry' }) +@Entity({ name: "d_b_oauth_auth_code_entry" }) export class DBOAuthAuthCodeEntry implements OAuthAuthCode { @PrimaryGeneratedColumn() id: number; @@ -23,8 +23,8 @@ export class DBOAuthAuthCodeEntry implements OAuthAuthCode { @Column({ type: "varchar", length: 1024, - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) redirectURI?: string; @@ -32,33 +32,33 @@ export class DBOAuthAuthCodeEntry implements OAuthAuthCode { type: "varchar", length: 128, }) - codeChallenge: string + codeChallenge: string; @Column({ type: "varchar", length: 10, }) - codeChallengeMethod: string + codeChallengeMethod: string; @Column({ - type: 'timestamp', - precision: 6 + type: "timestamp", + precision: 6, }) expiresAt: Date; - @ManyToOne(type => DBUser) + @ManyToOne((type) => DBUser) @JoinColumn() - user: DBUser + user: DBUser; @Column({ - type: 'simple-json', + type: "simple-json", nullable: false, }) - client: OAuthClient + client: OAuthClient; @Column({ - type: 'simple-json', + type: "simple-json", nullable: false, }) - scopes: OAuthScope[] -} \ No newline at end of file + scopes: OAuthScope[]; +} diff --git a/components/gitpod-db/src/typeorm/entity/db-one-time-secret.ts b/components/gitpod-db/src/typeorm/entity/db-one-time-secret.ts index e6b3ce26f330b0..77bab25b2a1294 100644 --- a/components/gitpod-db/src/typeorm/entity/db-one-time-secret.ts +++ b/components/gitpod-db/src/typeorm/entity/db-one-time-secret.ts @@ -21,17 +21,17 @@ export class DBOneTimeSecret implements OneTimeSecret { // Relies on the initialization of the var in OneTimeSecretDbImpl transformer: Transformer.compose( Transformer.SIMPLE_JSON([]), - Transformer.encrypted(() => encryptionService) - ) + Transformer.encrypted(() => encryptionService), + ), }) value: string; @Column({ - type: 'timestamp', - precision: 6 + type: "timestamp", + precision: 6, }) expirationTime: string; @Column() deleted: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-oss-allowlist.ts b/components/gitpod-db/src/typeorm/entity/db-oss-allowlist.ts index 3369bf583b416a..a3f74feab4204f 100644 --- a/components/gitpod-db/src/typeorm/entity/db-oss-allowlist.ts +++ b/components/gitpod-db/src/typeorm/entity/db-oss-allowlist.ts @@ -21,4 +21,4 @@ export class DBOssAllowList implements OssAllowList { @Column() deleted?: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-pending-github-event.ts b/components/gitpod-db/src/typeorm/entity/db-pending-github-event.ts index fae348f392a51b..71b70ccb25a3d8 100644 --- a/components/gitpod-db/src/typeorm/entity/db-pending-github-event.ts +++ b/components/gitpod-db/src/typeorm/entity/db-pending-github-event.ts @@ -16,7 +16,7 @@ export class DBPendingGithubEvent implements PendingGithubEvent { @Column() githubUserId: string; - @Column({type: 'timestamp', precision: 6}) + @Column({ type: "timestamp", precision: 6 }) creationDate: Date; @Column() diff --git a/components/gitpod-db/src/typeorm/entity/db-prebuild-info-entry.ts b/components/gitpod-db/src/typeorm/entity/db-prebuild-info-entry.ts index 9f9c8eaf042b9a..15f72012194895 100644 --- a/components/gitpod-db/src/typeorm/entity/db-prebuild-info-entry.ts +++ b/components/gitpod-db/src/typeorm/entity/db-prebuild-info-entry.ts @@ -11,12 +11,11 @@ import { TypeORM } from "../../typeorm/typeorm"; @Entity() export class DBPrebuildInfo { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) prebuildId: string; @Column({ - type: 'simple-json', + type: "simple-json", transformer: (() => { return { to(value: any): any { @@ -26,12 +25,10 @@ export class DBPrebuildInfo { try { const obj = JSON.parse(value); return PrebuildInfo.is(obj) ? obj : undefined; - } catch (error) { - } - } + } catch (error) {} + }, }; - })() + })(), }) info: PrebuildInfo; - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-prebuilt-workspace-updatable.ts b/components/gitpod-db/src/typeorm/entity/db-prebuilt-workspace-updatable.ts index 0c503d417eb811..c6fe1597008396 100644 --- a/components/gitpod-db/src/typeorm/entity/db-prebuilt-workspace-updatable.ts +++ b/components/gitpod-db/src/typeorm/entity/db-prebuilt-workspace-updatable.ts @@ -18,7 +18,6 @@ import { Transformer } from "../transformer"; @Index("ind_prebuiltWorkspaceId_isResolved", ["prebuiltWorkspaceId", "isResolved"]) @Entity() export class DBPrebuiltWorkspaceUpdatable implements PrebuiltWorkspaceUpdatable { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) id: string; @@ -32,8 +31,8 @@ export class DBPrebuiltWorkspaceUpdatable implements PrebuiltWorkspaceUpdatable repo: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) commitSHA?: string; @@ -43,23 +42,21 @@ export class DBPrebuiltWorkspaceUpdatable implements PrebuiltWorkspaceUpdatable @Column() installationId: string; - @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) contextUrl?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) issue?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) label?: string; - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-prebuilt-workspace.ts b/components/gitpod-db/src/typeorm/entity/db-prebuilt-workspace.ts index c18f6e181929c0..780147a36ffd27 100644 --- a/components/gitpod-db/src/typeorm/entity/db-prebuilt-workspace.ts +++ b/components/gitpod-db/src/typeorm/entity/db-prebuilt-workspace.ts @@ -13,10 +13,12 @@ import { PrebuildWorkspaceRateLimiterMigration1646739309660 } from "../migration @Entity() @Index("ind_ac4a9aece1a455da0dc653888f", ["cloneURL", "commit"]) -@Index(PrebuildWorkspaceRateLimiterMigration1646739309660.INDEX_NAME, PrebuildWorkspaceRateLimiterMigration1646739309660.FIELDS) +@Index( + PrebuildWorkspaceRateLimiterMigration1646739309660.INDEX_NAME, + PrebuildWorkspaceRateLimiterMigration1646739309660.FIELDS, +) // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBPrebuiltWorkspace implements PrebuiltWorkspace { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) id: string; @@ -27,15 +29,15 @@ export class DBPrebuiltWorkspace implements PrebuiltWorkspace { commit: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) @Index("ind_projectId") projectId?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) branch?: string; @@ -44,26 +46,26 @@ export class DBPrebuiltWorkspace implements PrebuiltWorkspace { state: PrebuiltWorkspaceState; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - default: () => 'CURRENT_TIMESTAMP(6)', - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + default: () => "CURRENT_TIMESTAMP(6)", + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) creationTime: string; @Column(TypeORM.WORKSPACE_ID_COLUMN_TYPE) - @Index('ind_buildWorkspaceId') + @Index("ind_buildWorkspaceId") buildWorkspaceId: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) snapshot?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) error?: string; } diff --git a/components/gitpod-db/src/typeorm/entity/db-project-env-vars.ts b/components/gitpod-db/src/typeorm/entity/db-project-env-vars.ts index c74adbfbb2ca4a..fadfaaaf1313f0 100644 --- a/components/gitpod-db/src/typeorm/entity/db-project-env-vars.ts +++ b/components/gitpod-db/src/typeorm/entity/db-project-env-vars.ts @@ -29,8 +29,8 @@ export class DBProjectEnvVar implements ProjectEnvVarWithValue { type: "simple-json", transformer: Transformer.compose( Transformer.SIMPLE_JSON([]), - Transformer.encrypted(() => encryptionService) - ) + Transformer.encrypted(() => encryptionService), + ), }) value: string; @@ -43,4 +43,4 @@ export class DBProjectEnvVar implements ProjectEnvVarWithValue { // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. @Column() deleted: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-project-info.ts b/components/gitpod-db/src/typeorm/entity/db-project-info.ts index b80a8e552c7091..8850a75086c56f 100644 --- a/components/gitpod-db/src/typeorm/entity/db-project-info.ts +++ b/components/gitpod-db/src/typeorm/entity/db-project-info.ts @@ -12,12 +12,11 @@ import { TypeORM } from "../../typeorm/typeorm"; @Entity() // on DB but not Typeorm: @Index("ind_dbsync", ["_lastModified"]) // DBSync export class DBProjectInfo { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) projectId: string; @Column({ - type: 'simple-json', + type: "simple-json", transformer: (() => { return { to(value: any): any { @@ -29,15 +28,14 @@ export class DBProjectInfo { if (Project.Overview.is(obj)) { return obj; } - } catch (error) { - } - } + } catch (error) {} + }, }; - })() + })(), }) overview: Project.Overview; // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. @Column() deleted: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-project.ts b/components/gitpod-db/src/typeorm/entity/db-project.ts index 68292ca003693d..dfa71878b58ae0 100644 --- a/components/gitpod-db/src/typeorm/entity/db-project.ts +++ b/components/gitpod-db/src/typeorm/entity/db-project.ts @@ -12,49 +12,49 @@ import { Transformer } from "../transformer"; @Entity() // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBProject { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) - id: string; + @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) + id: string; - @Column() - name: string; + @Column() + name: string; - @Column() - slug?: string; + @Column() + slug?: string; - @Index("ind_cloneUrl") - @Column() - cloneUrl: string; + @Index("ind_cloneUrl") + @Column() + cloneUrl: string; - @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED - }) - @Index("ind_teamId") - teamId?: string; + @Column({ + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, + }) + @Index("ind_teamId") + teamId?: string; - @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED - }) - @Index("ind_userId") - userId?: string; + @Column({ + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, + }) + @Index("ind_userId") + userId?: string; - @Column() - appInstallationId: string; + @Column() + appInstallationId: string; - @Column("simple-json", { nullable: true }) - config?: ProjectConfig; + @Column("simple-json", { nullable: true }) + config?: ProjectConfig; - @Column("simple-json", { nullable: true }) - settings?: ProjectSettings; + @Column("simple-json", { nullable: true }) + settings?: ProjectSettings; - @Column("varchar") - creationTime: string; + @Column("varchar") + creationTime: string; - // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. - @Column() - deleted: boolean; + // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. + @Column() + deleted: boolean; - @Column() - markedDeleted: boolean; -} \ No newline at end of file + @Column() + markedDeleted: boolean; +} diff --git a/components/gitpod-db/src/typeorm/entity/db-repository-whitelist.ts b/components/gitpod-db/src/typeorm/entity/db-repository-whitelist.ts index d76926d6d9898b..ca50d68df930f7 100644 --- a/components/gitpod-db/src/typeorm/entity/db-repository-whitelist.ts +++ b/components/gitpod-db/src/typeorm/entity/db-repository-whitelist.ts @@ -4,7 +4,6 @@ * See License-AGPL.txt in the project root for license information. */ - import { PrimaryColumn, Entity, Column } from "typeorm"; @Entity() @@ -12,19 +11,18 @@ export class DBRepositoryWhiteList { static readonly MIN_FEATURED_REPOSITORY_PRIO = 0; @PrimaryColumn({ - type: 'char', - length: 128 + type: "char", + length: 128, }) url: string; @Column({ - type: 'text' + type: "text", }) description?: string; @Column({ - default: 10 + default: 10, }) priority?: number; - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-snapshot.ts b/components/gitpod-db/src/typeorm/entity/db-snapshot.ts index 379f9ec686c554..437328f8c8d04d 100644 --- a/components/gitpod-db/src/typeorm/entity/db-snapshot.ts +++ b/components/gitpod-db/src/typeorm/entity/db-snapshot.ts @@ -11,23 +11,22 @@ import { TypeORM } from "../typeorm"; import { Transformer } from "../transformer"; @Entity() -@Index("ind_dbsync", ["creationTime"]) // DBSync +@Index("ind_dbsync", ["creationTime"]) // DBSync export class DBSnapshot implements Snapshot { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) id: string; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - default: () => 'CURRENT_TIMESTAMP(6)', - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + default: () => "CURRENT_TIMESTAMP(6)", + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) creationTime: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) availableTime?: string; @@ -43,14 +42,14 @@ export class DBSnapshot implements Snapshot { @Column({ // because we introduced this as an afterthought the default is 'available' - default: 'available', + default: "available", }) @Index("ind_state") state: SnapshotState; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) message?: string; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-subscription.ts b/components/gitpod-db/src/typeorm/entity/db-subscription.ts index 61ab812c6e2be3..7e97bdcdef2604 100644 --- a/components/gitpod-db/src/typeorm/entity/db-subscription.ts +++ b/components/gitpod-db/src/typeorm/entity/db-subscription.ts @@ -13,7 +13,6 @@ import { Subscription, PaymentData } from "@gitpod/gitpod-protocol/lib/accountin @Index("ind_user_paymentReference", ["userId", "paymentReference"]) // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBSubscription implements Subscription { - @PrimaryColumn("uuid") uid: string; @@ -24,30 +23,30 @@ export class DBSubscription implements Subscription { startDate: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) endDate?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) cancellationDate?: string; - @Column('double') + @Column("double") amount: number; - @Column('double', { nullable: true }) + @Column("double", { nullable: true }) firstMonthAmount?: number; - @Column({ default: 'free' }) + @Column({ default: "free" }) @Index("ind_planId") planId: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) paymentReference?: string; @@ -55,14 +54,14 @@ export class DBSubscription implements Subscription { paymentData?: PaymentData; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) - @Index('ind_teamSubscriptionSlotId') + @Index("ind_teamSubscriptionSlotId") teamSubscriptionSlotId?: string; @Column({ - default: false + default: false, }) deleted?: boolean; } @@ -70,12 +69,11 @@ export class DBSubscription implements Subscription { @Entity() // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBSubscriptionAdditionalData { - @PrimaryColumn() paymentReference: string; @Column({ - default: 0 + default: 0, }) mrr: number; @@ -86,21 +84,21 @@ export class DBSubscriptionAdditionalData { lastInvoiceAmount: number; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) lastInvoice?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) nextBilling?: string; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) lastModified?: string; } diff --git a/components/gitpod-db/src/typeorm/entity/db-team-membership-invite.ts b/components/gitpod-db/src/typeorm/entity/db-team-membership-invite.ts index 4e23d626c65535..f2aed54a1b1651 100644 --- a/components/gitpod-db/src/typeorm/entity/db-team-membership-invite.ts +++ b/components/gitpod-db/src/typeorm/entity/db-team-membership-invite.ts @@ -29,12 +29,12 @@ export class DBTeamMembershipInvite { invalidationTime: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) invitedEmail?: string; // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. @Column() deleted: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-team-membership.ts b/components/gitpod-db/src/typeorm/entity/db-team-membership.ts index 979d81d36f541a..090783d583310f 100644 --- a/components/gitpod-db/src/typeorm/entity/db-team-membership.ts +++ b/components/gitpod-db/src/typeorm/entity/db-team-membership.ts @@ -11,24 +11,24 @@ import { TypeORM } from "../typeorm"; @Entity() // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBTeamMembership { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) - id: string; + @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) + id: string; - @Column(TypeORM.UUID_COLUMN_TYPE) - @Index("ind_teamId") - teamId: string; + @Column(TypeORM.UUID_COLUMN_TYPE) + @Index("ind_teamId") + teamId: string; - @Column(TypeORM.UUID_COLUMN_TYPE) - @Index("ind_userId") - userId: string; + @Column(TypeORM.UUID_COLUMN_TYPE) + @Index("ind_userId") + userId: string; - @Column("varchar") - role: TeamMemberRole; + @Column("varchar") + role: TeamMemberRole; - @Column("varchar") - creationTime: string; + @Column("varchar") + creationTime: string; - // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. - @Column() - deleted: boolean; -} \ No newline at end of file + // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. + @Column() + deleted: boolean; +} diff --git a/components/gitpod-db/src/typeorm/entity/db-team-subscription-slot.ts b/components/gitpod-db/src/typeorm/entity/db-team-subscription-slot.ts index 25ed2de359bb36..1381d78aeda62d 100644 --- a/components/gitpod-db/src/typeorm/entity/db-team-subscription-slot.ts +++ b/components/gitpod-db/src/typeorm/entity/db-team-subscription-slot.ts @@ -21,27 +21,27 @@ export class DBTeamSubscriptionSlot implements TeamSubscriptionSlot { @Column({ ...TypeORM.UUID_COLUMN_TYPE, - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) assigneeId?: string; @Column({ ...TypeORM.UUID_COLUMN_TYPE, - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) subscriptionId?: string; @Column({ - type: 'simple-json', - nullable: true + type: "simple-json", + nullable: true, }) assigneeIdentifier?: AssigneeIdentifier; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) cancellationDate?: string; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-team-subscription.ts b/components/gitpod-db/src/typeorm/entity/db-team-subscription.ts index e2d7d3dbf307d0..fe03b37e7d2c49 100644 --- a/components/gitpod-db/src/typeorm/entity/db-team-subscription.ts +++ b/components/gitpod-db/src/typeorm/entity/db-team-subscription.ts @@ -16,7 +16,6 @@ import { Transformer } from "../../typeorm/transformer"; @Index("ind_user_startdate", ["userId", "startDate"]) // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBTeamSubscription implements TeamSubscription { - @PrimaryColumn("uuid") id: string; @@ -30,30 +29,30 @@ export class DBTeamSubscription implements TeamSubscription { startDate: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) endDate?: string; @Column() planId: string; - @Column('int') + @Column("int") quantity: number; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) cancellationDate?: string; @Column({ - default: false + default: false, }) deleted?: boolean; @Column({ - default: false + default: false, }) excludeFromMoreResources: boolean; } diff --git a/components/gitpod-db/src/typeorm/entity/db-team.ts b/components/gitpod-db/src/typeorm/entity/db-team.ts index ca147c6d1a5f03..7a7683632e96d3 100644 --- a/components/gitpod-db/src/typeorm/entity/db-team.ts +++ b/components/gitpod-db/src/typeorm/entity/db-team.ts @@ -11,22 +11,22 @@ import { TypeORM } from "../typeorm"; @Entity() // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBTeam implements Team { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) - id: string; + @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) + id: string; - @Column("varchar") - name: string; + @Column("varchar") + name: string; - @Column("varchar") - slug: string; + @Column("varchar") + slug: string; - @Column("varchar") - creationTime: string; + @Column("varchar") + creationTime: string; - @Column() - markedDeleted?: boolean; + @Column() + markedDeleted?: boolean; - // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. - @Column() - deleted: boolean; -} \ No newline at end of file + // This column triggers the db-sync deletion mechanism. It's not intended for public consumption. + @Column() + deleted: boolean; +} diff --git a/components/gitpod-db/src/typeorm/entity/db-terms-acceptance-entry.ts b/components/gitpod-db/src/typeorm/entity/db-terms-acceptance-entry.ts index a6aff2cc0950cd..be05f54129210c 100644 --- a/components/gitpod-db/src/typeorm/entity/db-terms-acceptance-entry.ts +++ b/components/gitpod-db/src/typeorm/entity/db-terms-acceptance-entry.ts @@ -15,7 +15,6 @@ import { TermsAcceptanceEntry } from "@gitpod/gitpod-protocol"; @Entity() // on DB but not Typeorm: @Index("ind_userId", ["userId"]) // DBSync export class DBTermsAcceptanceEntry implements TermsAcceptanceEntry { - @PrimaryColumn() userId: string; diff --git a/components/gitpod-db/src/typeorm/entity/db-theia-plugin.ts b/components/gitpod-db/src/typeorm/entity/db-theia-plugin.ts index 42555ed4003116..00f26cb4f06c93 100644 --- a/components/gitpod-db/src/typeorm/entity/db-theia-plugin.ts +++ b/components/gitpod-db/src/typeorm/entity/db-theia-plugin.ts @@ -19,14 +19,14 @@ export class DBTheiaPlugin implements TheiaPlugin { pluginName: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) pluginId?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) userId?: string; @@ -37,11 +37,11 @@ export class DBTheiaPlugin implements TheiaPlugin { path: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) hash?: string; @Column() state: TheiaPlugin.State; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-token-entry.ts b/components/gitpod-db/src/typeorm/entity/db-token-entry.ts index c3f6d344fab98d..fdc111c9582c40 100644 --- a/components/gitpod-db/src/typeorm/entity/db-token-entry.ts +++ b/components/gitpod-db/src/typeorm/entity/db-token-entry.ts @@ -18,16 +18,16 @@ export class DBTokenEntry implements TokenEntry { @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) uid: string; - @Column('varchar') + @Column("varchar") authProviderId: string; - @Column('varchar') + @Column("varchar") authId: string; - @Index('ind_expiryDate') + @Index("ind_expiryDate") @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) expiryDate?: string; @@ -39,11 +39,11 @@ export class DBTokenEntry implements TokenEntry { transformer: Transformer.compose( Transformer.SIMPLE_JSON([]), // Relies on the initialization of the var in UserDbImpl - Transformer.encrypted(() => encryptionService) - ) + Transformer.encrypted(() => encryptionService), + ), }) token: Token; @Column() deleted?: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-user-env-vars.ts b/components/gitpod-db/src/typeorm/entity/db-user-env-vars.ts index 0244b28d4f9354..753f9caeea783b 100644 --- a/components/gitpod-db/src/typeorm/entity/db-user-env-vars.ts +++ b/components/gitpod-db/src/typeorm/entity/db-user-env-vars.ts @@ -33,8 +33,8 @@ export class DBUserEnvVar implements UserEnvVar { // Relies on the initialization of the var in UserDbImpl transformer: Transformer.compose( Transformer.SIMPLE_JSON([]), - Transformer.encrypted(() => encryptionService) - ) + Transformer.encrypted(() => encryptionService), + ), }) value: string; @@ -43,4 +43,4 @@ export class DBUserEnvVar implements UserEnvVar { @Column() deleted?: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-user-message-view-entry.ts b/components/gitpod-db/src/typeorm/entity/db-user-message-view-entry.ts index 2497022e46630b..e6d47c397857dc 100644 --- a/components/gitpod-db/src/typeorm/entity/db-user-message-view-entry.ts +++ b/components/gitpod-db/src/typeorm/entity/db-user-message-view-entry.ts @@ -9,8 +9,8 @@ import { TypeORM } from "../typeorm"; import { Transformer } from "../transformer"; @Entity() -@Index("ind_dbsync", ["viewedAt"]) // DBSync -export class DBUserMessageViewEntry { +@Index("ind_dbsync", ["viewedAt"]) // DBSync +export class DBUserMessageViewEntry { @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) userId: string; @@ -18,11 +18,10 @@ export class DBUserMessageViewEntry { userMessageId: string; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - default: () => 'CURRENT_TIMESTAMP(6)', - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + default: () => "CURRENT_TIMESTAMP(6)", + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) viewedAt: string; - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-user-storage-resource.ts b/components/gitpod-db/src/typeorm/entity/db-user-storage-resource.ts index 4023af1234161d..ebaf22baa44cf7 100644 --- a/components/gitpod-db/src/typeorm/entity/db-user-storage-resource.ts +++ b/components/gitpod-db/src/typeorm/entity/db-user-storage-resource.ts @@ -9,11 +9,11 @@ import { TypeORM } from "../typeorm"; @Entity() // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync -export class DBUserStorageResource { +export class DBUserStorageResource { @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) userId: string; - @PrimaryColumn('varchar') + @PrimaryColumn("varchar") uri: string; @Column("text") diff --git a/components/gitpod-db/src/typeorm/entity/db-user.ts b/components/gitpod-db/src/typeorm/entity/db-user.ts index 69108305a26e4c..21097ca8ae4839 100644 --- a/components/gitpod-db/src/typeorm/entity/db-user.ts +++ b/components/gitpod-db/src/typeorm/entity/db-user.ts @@ -6,7 +6,7 @@ import { PrimaryColumn, Entity, Column, OneToMany, JoinColumn, Index } from "typeorm"; import { User, RoleOrPermission, AdditionalUserData, UserFeatureSettings } from "@gitpod/gitpod-protocol"; -import { DBIdentity } from './db-identity'; +import { DBIdentity } from "./db-identity"; import { TypeORM } from "../typeorm"; import { Transformer } from "../transformer"; @@ -21,64 +21,61 @@ export class DBUser implements User { creationDate: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) avatarUrl?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) name?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) fullName?: string; - @OneToMany(type => DBIdentity, - identity => identity.user, - { - eager: true, - cascade: ["insert", "update"], // we do delete on our own - } - ) + @OneToMany((type) => DBIdentity, (identity) => identity.user, { + eager: true, + cascade: ["insert", "update"], // we do delete on our own + }) @JoinColumn() identities: DBIdentity[]; @Column({ - default: false + default: false, }) blocked?: boolean; @Column({ - type: 'simple-json', - nullable: true + type: "simple-json", + nullable: true, }) featureFlags?: UserFeatureSettings; @Column({ - type: 'simple-json', - nullable: true + type: "simple-json", + nullable: true, }) rolesOrPermissions?: RoleOrPermission[]; @Column({ - default: false + default: false, }) markedDeleted?: boolean; // TODO CLEANUP DB: delete after all usages have been deleted @Column({ - default: false + default: false, }) noReleasePeriod?: boolean; @Column({ - type: 'simple-json', - nullable: true + type: "simple-json", + nullable: true, }) additionalData?: AdditionalUserData; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-workspace-cluster.ts b/components/gitpod-db/src/typeorm/entity/db-workspace-cluster.ts index 24499430ccaf74..6ff2a3255f8e62 100644 --- a/components/gitpod-db/src/typeorm/entity/db-workspace-cluster.ts +++ b/components/gitpod-db/src/typeorm/entity/db-workspace-cluster.ts @@ -5,7 +5,12 @@ */ import { PrimaryColumn, Column, Entity, Index } from "typeorm"; -import { AdmissionConstraint, TLSConfig, WorkspaceCluster, WorkspaceClusterState } from "@gitpod/gitpod-protocol/lib/workspace-cluster"; +import { + AdmissionConstraint, + TLSConfig, + WorkspaceCluster, + WorkspaceClusterState, +} from "@gitpod/gitpod-protocol/lib/workspace-cluster"; import { ValueTransformer } from "typeorm/decorator/options/ValueTransformer"; @Entity() @@ -24,7 +29,7 @@ export class DBWorkspaceCluster implements WorkspaceCluster { transformer: (() => { const defaultValue = {}; const jsonifiedDefault = JSON.stringify(defaultValue); - return { + return { // tls | undefined => | "{}" to(value: any): any { if (!value) { @@ -38,9 +43,9 @@ export class DBWorkspaceCluster implements WorkspaceCluster { return undefined; } return JSON.parse(value); - } + }, }; - })() + })(), }) tls?: TLSConfig; @@ -65,7 +70,7 @@ export class DBWorkspaceCluster implements WorkspaceCluster { transformer: (() => { const defaultValue: AdmissionConstraint[] = []; const jsonifiedDefault = JSON.stringify(defaultValue); - return { + return { to(value: any): any { if (!value) { return jsonifiedDefault; @@ -74,9 +79,9 @@ export class DBWorkspaceCluster implements WorkspaceCluster { }, from(value: any): any { return JSON.parse(value); - } + }, }; - })() + })(), }) admissionConstraints?: AdmissionConstraint[]; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-workspace-instance-user.ts b/components/gitpod-db/src/typeorm/entity/db-workspace-instance-user.ts index b61383309c74af..d25ddc516616d4 100644 --- a/components/gitpod-db/src/typeorm/entity/db-workspace-instance-user.ts +++ b/components/gitpod-db/src/typeorm/entity/db-workspace-instance-user.ts @@ -9,11 +9,9 @@ import { Column, Entity, PrimaryColumn } from "typeorm"; import { TypeORM } from "../typeorm"; import { Transformer } from "../transformer"; - @Entity() // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBWorkspaceInstanceUser implements WorkspaceInstanceUser { - @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) instanceId: string; @@ -21,15 +19,15 @@ export class DBWorkspaceInstanceUser implements WorkspaceInstanceUser { userId: string; @Column({ - type: 'timestamp', + type: "timestamp", precision: 6, - default: () => 'CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)', - transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP + default: () => "CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)", + transformer: Transformer.MAP_ISO_STRING_TO_TIMESTAMP_DROP, }) lastSeen: string; @Column({ - default: false + default: false, }) wasClosed: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/entity/db-workspace-instance.ts b/components/gitpod-db/src/typeorm/entity/db-workspace-instance.ts index 770f43f7f9b03c..8252a403656998 100644 --- a/components/gitpod-db/src/typeorm/entity/db-workspace-instance.ts +++ b/components/gitpod-db/src/typeorm/entity/db-workspace-instance.ts @@ -6,14 +6,19 @@ import { PrimaryColumn, Column, Index, Entity } from "typeorm"; -import { WorkspaceInstance, WorkspaceInstanceStatus, WorkspaceInstancePhase, WorkspaceInstanceConfiguration, ImageBuildInfo } from "@gitpod/gitpod-protocol"; +import { + WorkspaceInstance, + WorkspaceInstanceStatus, + WorkspaceInstancePhase, + WorkspaceInstanceConfiguration, + ImageBuildInfo, +} from "@gitpod/gitpod-protocol"; import { TypeORM } from "../typeorm"; import { Transformer } from "../transformer"; - @Entity() -@Index("ind_find_wsi_ws_in_period", ['workspaceId', 'startedTime', 'stoppedTime']) // findInstancesWithWorkspaceInPeriod -@Index("ind_phasePersisted_region", ['phasePersisted', 'region']) // findInstancesByPhaseAndRegion +@Index("ind_find_wsi_ws_in_period", ["workspaceId", "startedTime", "stoppedTime"]) // findInstancesWithWorkspaceInPeriod +@Index("ind_phasePersisted_region", ["phasePersisted", "region"]) // findInstancesByPhaseAndRegion // on DB but not Typeorm: @Index("ind_lastModified", ["_lastModified"]) // DBSync export class DBWorkspaceInstance implements WorkspaceInstance { @PrimaryColumn(TypeORM.UUID_COLUMN_TYPE) @@ -30,14 +35,14 @@ export class DBWorkspaceInstance implements WorkspaceInstance { creationTime: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) startedTime?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) deployedTime?: string; @@ -46,8 +51,8 @@ export class DBWorkspaceInstance implements WorkspaceInstance { * began to shut down on the cluster. */ @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) stoppingTime?: string; @@ -56,8 +61,8 @@ export class DBWorkspaceInstance implements WorkspaceInstance { * was actually stopped on the cluster. */ @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) stoppedTime?: string; @@ -67,7 +72,7 @@ export class DBWorkspaceInstance implements WorkspaceInstance { @Column() workspaceImage: string; - @Column('json') + @Column("json") status: WorkspaceInstanceStatus; /** @@ -83,11 +88,11 @@ export class DBWorkspaceInstance implements WorkspaceInstance { deleted?: boolean; @Column({ - type: 'simple-json', + type: "simple-json", nullable: true, }) configuration?: WorkspaceInstanceConfiguration; @Column("simple-json", { nullable: true }) imageBuildInfo?: ImageBuildInfo; -} \ 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 faa032085683c1..ff8e4ad337de07 100644 --- a/components/gitpod-db/src/typeorm/entity/db-workspace.ts +++ b/components/gitpod-db/src/typeorm/entity/db-workspace.ts @@ -6,7 +6,14 @@ import { PrimaryColumn, Column, Entity, Index } from "typeorm"; -import { Workspace, WorkspaceConfig, WorkspaceContext, WorkspaceImageSource, WorkspaceType, WorkspaceSoftDeletion } from "@gitpod/gitpod-protocol"; +import { + Workspace, + WorkspaceConfig, + WorkspaceContext, + WorkspaceImageSource, + WorkspaceType, + WorkspaceSoftDeletion, +} from "@gitpod/gitpod-protocol"; import { TypeORM } from "../typeorm"; import { Transformer } from "../transformer"; @@ -19,7 +26,7 @@ export class DBWorkspace implements Workspace { id: string; @Column("varchar") - @Index('ind_creationTime') + @Index("ind_creationTime") creationTime: string; @Column(TypeORM.UUID_COLUMN_TYPE) @@ -30,8 +37,8 @@ export class DBWorkspace implements Workspace { contextURL: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) projectId?: string; @@ -48,21 +55,21 @@ export class DBWorkspace implements Workspace { imageSource?: WorkspaceImageSource; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) - imageNameResolved?: string + imageNameResolved?: string; @Column() - baseImageNameResolved?: string + baseImageNameResolved?: string; @Column({ - default: false + default: false, }) shareable?: boolean; @Column({ - default: 'regular' + default: "regular", }) type: WorkspaceType; @@ -70,14 +77,14 @@ export class DBWorkspace implements Workspace { softDeleted?: WorkspaceSoftDeletion; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) softDeletedTime?: string; @Column({ - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) contentDeletedTime?: string; @@ -86,23 +93,23 @@ export class DBWorkspace implements Workspace { deleted?: boolean; @Column({ - default: false + default: false, }) pinned?: boolean; @Column({ ...TypeORM.UUID_COLUMN_TYPE, - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) - @Index('ind_basedOnPrebuildId') + @Index("ind_basedOnPrebuildId") basedOnPrebuildId?: string; @Column({ ...TypeORM.UUID_COLUMN_TYPE, - default: '', - transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED + default: "", + transformer: Transformer.MAP_EMPTY_STR_TO_UNDEFINED, }) - @Index('ind_basedOnSnapshotId') + @Index("ind_basedOnSnapshotId") basedOnSnapshotId?: string; } diff --git a/components/gitpod-db/src/typeorm/installation-admin-db-impl.ts b/components/gitpod-db/src/typeorm/installation-admin-db-impl.ts index 8f0157e9d7500c..e90fdf0e798e74 100644 --- a/components/gitpod-db/src/typeorm/installation-admin-db-impl.ts +++ b/components/gitpod-db/src/typeorm/installation-admin-db-impl.ts @@ -4,12 +4,12 @@ * See License-AGPL.txt in the project root for license information. */ -import { inject, injectable, } from 'inversify'; -import { InstallationAdmin, InstallationAdminSettings } from '@gitpod/gitpod-protocol'; -import { Repository } from 'typeorm'; -import { TypeORM } from './typeorm'; -import { InstallationAdminDB } from '../installation-admin-db'; -import { DBInstallationAdmin } from './entity/db-installation-admin'; +import { inject, injectable } from "inversify"; +import { InstallationAdmin, InstallationAdminSettings } from "@gitpod/gitpod-protocol"; +import { Repository } from "typeorm"; +import { TypeORM } from "./typeorm"; +import { InstallationAdminDB } from "../installation-admin-db"; +import { DBInstallationAdmin } from "./entity/db-installation-admin"; @injectable() export class TypeORMInstallationAdminImpl implements InstallationAdminDB { @@ -54,7 +54,7 @@ export class TypeORMInstallationAdminImpl implements InstallationAdminDB { const record = await this.getData(); record.settings = { ...settings, - } + }; const repo = await this.getInstallationAdminRepo(); await repo.save(record); diff --git a/components/gitpod-db/src/typeorm/license-db-impl.ts b/components/gitpod-db/src/typeorm/license-db-impl.ts index 02d96a5268bae1..bcfa2e8372b269 100644 --- a/components/gitpod-db/src/typeorm/license-db-impl.ts +++ b/components/gitpod-db/src/typeorm/license-db-impl.ts @@ -13,7 +13,6 @@ import { DBLicenseKey } from "./entity/db-license-key"; @injectable() export class LicenseDBImpl implements LicenseDB { - @inject(TypeORM) typeorm: TypeORM; protected async getRepo(): Promise> { @@ -35,7 +34,7 @@ export class LicenseDBImpl implements LicenseDB { async get(): Promise { const repo = await this.getRepo(); const dbobj = await repo.findOne({ - order: { installationTime: "DESC" } + order: { installationTime: "DESC" }, }); if (!dbobj) { return; @@ -43,5 +42,4 @@ export class LicenseDBImpl implements LicenseDB { return dbobj.key; } - -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/migrate-migrations-0_2_0.ts b/components/gitpod-db/src/typeorm/migrate-migrations-0_2_0.ts index c7d076e03b871e..060f875edbedd7 100644 --- a/components/gitpod-db/src/typeorm/migrate-migrations-0_2_0.ts +++ b/components/gitpod-db/src/typeorm/migrate-migrations-0_2_0.ts @@ -13,7 +13,6 @@ import { columnExists, tableExists } from "./migration/helper/helper"; * entrypoint that triggers this "meta-migration", which re-uses the common migration interface. */ export class MigrateMigrations0_2_0 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await tableExists(queryRunner, "migrations")) { const idColumnExists = await columnExists(queryRunner, "migrations", "id"); @@ -39,5 +38,4 @@ export class MigrateMigrations0_2_0 implements MigrationInterface { } } } - } diff --git a/components/gitpod-db/src/typeorm/migration/1592203031938-Baseline.ts b/components/gitpod-db/src/typeorm/migration/1592203031938-Baseline.ts index 3423bfe8a7d435..a5f1beab244052 100644 --- a/components/gitpod-db/src/typeorm/migration/1592203031938-Baseline.ts +++ b/components/gitpod-db/src/typeorm/migration/1592203031938-Baseline.ts @@ -9,7 +9,6 @@ import { MigrationInterface, QueryRunner } from "typeorm"; import { BUILTIN_WORKSPACE_PROBE_USER_ID } from "../../user-db"; export class Baseline1592203031938 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const createTables = [ `CREATE TABLE IF NOT EXISTS d_b_account_entry ( id int(11) DEFAULT NULL, userId char(36) NOT NULL, amount double NOT NULL, date varchar(255) NOT NULL, expiryDate varchar(255) NOT NULL DEFAULT '', kind char(7) NOT NULL, description text, uid char(36) NOT NULL, creditId char(36) DEFAULT NULL, _lastModified timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (uid), KEY ind_dbsync (_lastModified), KEY ind_expiryDate (expiryDate)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`, @@ -54,34 +53,76 @@ export class Baseline1592203031938 implements MigrationInterface { const count = (await queryRunner.query(`SELECT COUNT(1) AS cnt FROM d_b_repository_white_list`))[0].cnt; if (Number.parseInt(count) < 1) { const entries = [ - { url: 'https://github.com/gitpod-io/go-gin-app.git', description: '**Go** - A simple web app implemented in Go and Gin', priority: 7 }, - { url: 'https://github.com/gitpod-io/rails_sample_app', description: '**Ruby on Rails** - Tutorial sample application', priority: 6 }, - { url: 'https://github.com/gitpod-io/NextSimpleStarter.git', description: '**JavaScript** - Simple PWA boilerplate with Next.js and Redux', priority: 8 }, - { url: 'https://github.com/gitpod-io/django-locallibrary-tutorial', description: '**Python** - Tutorial "Local Library" website written in Django', priority: 10 }, - { url: 'https://github.com/gitpod-io/gs-spring-boot.git', description: '**Java** - Building an Application with Spring Boot', priority: 9 }, - { url: 'https://github.com/gitpod-io/symfony-demo.git', description: '**PHP** - Symfony Demo Application', priority: 5 }, - { url: 'https://github.com/theia-ide/theia.git', description: '**Typescript** - Deep dive into Gitpod\\\'s open-source IDE, Theia.', priority: 4 } - ] - await Promise.all(entries.map(e => queryRunner.query(`INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES (?, ?, ?)`, [e.url, e.description, e.priority]))); + { + url: "https://github.com/gitpod-io/go-gin-app.git", + description: "**Go** - A simple web app implemented in Go and Gin", + priority: 7, + }, + { + url: "https://github.com/gitpod-io/rails_sample_app", + description: "**Ruby on Rails** - Tutorial sample application", + priority: 6, + }, + { + url: "https://github.com/gitpod-io/NextSimpleStarter.git", + description: "**JavaScript** - Simple PWA boilerplate with Next.js and Redux", + priority: 8, + }, + { + url: "https://github.com/gitpod-io/django-locallibrary-tutorial", + description: '**Python** - Tutorial "Local Library" website written in Django', + priority: 10, + }, + { + url: "https://github.com/gitpod-io/gs-spring-boot.git", + description: "**Java** - Building an Application with Spring Boot", + priority: 9, + }, + { + url: "https://github.com/gitpod-io/symfony-demo.git", + description: "**PHP** - Symfony Demo Application", + priority: 5, + }, + { + url: "https://github.com/theia-ide/theia.git", + description: "**Typescript** - Deep dive into Gitpod\\'s open-source IDE, Theia.", + priority: 4, + }, + ]; + await Promise.all( + entries.map((e) => + queryRunner.query( + `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES (?, ?, ?)`, + [e.url, e.description, e.priority], + ), + ), + ); } } // domain filters { const entries = [ - { domain: 'tempail.com', negative: true }, - { domain: 'ezehe.com', negative: true }, - { domain: 'radiodale.com', negative: true } + { domain: "tempail.com", negative: true }, + { domain: "ezehe.com", negative: true }, + { domain: "radiodale.com", negative: true }, ]; - const values = entries.map(e => `('${e.domain}', '${e.negative ? 1 : 0}')`).join(","); + const values = entries.map((e) => `('${e.domain}', '${e.negative ? 1 : 0}')`).join(","); await queryRunner.query(`INSERT IGNORE INTO d_b_email_domain_filter (domain, negative) VALUES ${values}`); } // probe user { - const exists = (await queryRunner.query(`SELECT COUNT(1) AS cnt FROM d_b_user WHERE id = 'builtin-user-workspace-probe-0000000'`))[0].cnt == 1; + const exists = + ( + await queryRunner.query( + `SELECT COUNT(1) AS cnt FROM d_b_user WHERE id = 'builtin-user-workspace-probe-0000000'`, + ) + )[0].cnt == 1; if (!exists) { - await queryRunner.query(`INSERT IGNORE INTO d_b_user (id, creationDate, avatarUrl, name, fullName) VALUES ('${BUILTIN_WORKSPACE_PROBE_USER_ID}', '${new Date().toISOString()}', '', 'builtin-workspace-prober', '')`) + await queryRunner.query( + `INSERT IGNORE INTO d_b_user (id, creationDate, avatarUrl, name, fullName) VALUES ('${BUILTIN_WORKSPACE_PROBE_USER_ID}', '${new Date().toISOString()}', '', 'builtin-workspace-prober', '')`, + ); } } } @@ -89,5 +130,4 @@ export class Baseline1592203031938 implements MigrationInterface { public async down(queryRunner: QueryRunner): Promise { // this is a one-way idempotent 'migration', no rollback possible for a nonempty DB } - } diff --git a/components/gitpod-db/src/typeorm/migration/1593167873419-LicenseDB.ts b/components/gitpod-db/src/typeorm/migration/1593167873419-LicenseDB.ts index a015d187c879a2..db64c390b67656 100644 --- a/components/gitpod-db/src/typeorm/migration/1593167873419-LicenseDB.ts +++ b/components/gitpod-db/src/typeorm/migration/1593167873419-LicenseDB.ts @@ -4,13 +4,14 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { tableExists } from "./helper/helper"; export class LicenseDB1593167873419 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_license_key` (`id` char(36) NOT NULL, `installationTime` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `key` text NOT NULL, PRIMARY KEY(`id`)) ENGINE=InnoDB"); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_license_key` (`id` char(36) NOT NULL, `installationTime` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), `key` text NOT NULL, PRIMARY KEY(`id`)) ENGINE=InnoDB", + ); } public async down(queryRunner: QueryRunner): Promise { @@ -18,5 +19,4 @@ export class LicenseDB1593167873419 implements MigrationInterface { await queryRunner.query("DROP TABLE `d_b_license_key`"); } } - } diff --git a/components/gitpod-db/src/typeorm/migration/1593180029504-AuthProviderEntry.ts b/components/gitpod-db/src/typeorm/migration/1593180029504-AuthProviderEntry.ts index ce0ea350c461d0..286c77a888e346 100644 --- a/components/gitpod-db/src/typeorm/migration/1593180029504-AuthProviderEntry.ts +++ b/components/gitpod-db/src/typeorm/migration/1593180029504-AuthProviderEntry.ts @@ -4,14 +4,15 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { tableExists } from "./helper/helper"; export class AuthProviderEntry1593180029504 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await tableExists(queryRunner, "d_b_auth_provider_entry"))) { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_auth_provider_entry` (`id` varchar(255) NOT NULL, `ownerId` char(36) NOT NULL, `status` varchar(25) NOT NULL, `host` varchar(255) NOT NULL, `type` varchar(100) NOT NULL, oauth text 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)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_auth_provider_entry` (`id` varchar(255) NOT NULL, `ownerId` char(36) NOT NULL, `status` varchar(25) NOT NULL, `host` varchar(255) NOT NULL, `type` varchar(100) NOT NULL, oauth text 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)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", + ); await queryRunner.query("CREATE INDEX `ind_lastModified` ON `d_b_auth_provider_entry` (_lastModified)"); await queryRunner.query("CREATE INDEX `ind_ownerId` ON `d_b_auth_provider_entry` (ownerId)"); await queryRunner.query("CREATE INDEX `ind_host` ON `d_b_auth_provider_entry` (host)"); @@ -28,5 +29,4 @@ export class AuthProviderEntry1593180029504 implements MigrationInterface { await queryRunner.query("DROP TABLE `d_b_auth_provider_entry`"); } } - } diff --git a/components/gitpod-db/src/typeorm/migration/1600693766152-ExpandScopes.ts b/components/gitpod-db/src/typeorm/migration/1600693766152-ExpandScopes.ts index 84cf0326b49ae2..d67d4755b6d6a8 100644 --- a/components/gitpod-db/src/typeorm/migration/1600693766152-ExpandScopes.ts +++ b/components/gitpod-db/src/typeorm/migration/1600693766152-ExpandScopes.ts @@ -4,15 +4,12 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class ExpandScopes1600693766152 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { await queryRunner.query("ALTER TABLE d_b_gitpod_token MODIFY COLUMN scopes text"); } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1606163573103-TermsAcceptance.ts b/components/gitpod-db/src/typeorm/migration/1606163573103-TermsAcceptance.ts index b72757b25a5c88..f819aafa23bd52 100644 --- a/components/gitpod-db/src/typeorm/migration/1606163573103-TermsAcceptance.ts +++ b/components/gitpod-db/src/typeorm/migration/1606163573103-TermsAcceptance.ts @@ -4,18 +4,17 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { tableExists } from "./helper/helper"; export class TermsAcceptance1606163573103 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await tableExists(queryRunner, "d_b_terms_acceptance_entry"))) { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_terms_acceptance_entry` ( `userId` char(36) NOT NULL, `termsRevision` varchar(255) NOT NULL, `acceptionTime` varchar(255) NOT NULL, PRIMARY KEY (userId) ) ENGINE=InnoDB"); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_terms_acceptance_entry` ( `userId` char(36) NOT NULL, `termsRevision` varchar(255) NOT NULL, `acceptionTime` varchar(255) NOT NULL, PRIMARY KEY (userId) ) ENGINE=InnoDB", + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1607429613319-TermsAcceptance-lastModified.ts b/components/gitpod-db/src/typeorm/migration/1607429613319-TermsAcceptance-lastModified.ts index 6d4a9bd0ca92ba..57f1b96afc5cd4 100644 --- a/components/gitpod-db/src/typeorm/migration/1607429613319-TermsAcceptance-lastModified.ts +++ b/components/gitpod-db/src/typeorm/migration/1607429613319-TermsAcceptance-lastModified.ts @@ -4,20 +4,19 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { tableExists, columnExists } from "./helper/helper"; export class Foo1607429613319 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await tableExists(queryRunner, "d_b_terms_acceptance_entry")) { if (!(await columnExists(queryRunner, "d_b_terms_acceptance_entry", "_lastModified"))) { - await queryRunner.query("ALTER TABLE d_b_terms_acceptance_entry ADD COLUMN _lastModified timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)"); + await queryRunner.query( + "ALTER TABLE d_b_terms_acceptance_entry ADD COLUMN _lastModified timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6)", + ); } } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1612448906950-ExampleRepositories.ts b/components/gitpod-db/src/typeorm/migration/1612448906950-ExampleRepositories.ts index 274fcb5189eda9..deb1d935386924 100644 --- a/components/gitpod-db/src/typeorm/migration/1612448906950-ExampleRepositories.ts +++ b/components/gitpod-db/src/typeorm/migration/1612448906950-ExampleRepositories.ts @@ -3,39 +3,109 @@ * 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 { MigrationInterface, QueryRunner } from "typeorm"; export class ExampleRepositories1612448906950 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const oldEntries = [ - { url: 'https://github.com/gitpod-io/django-locallibrary-tutorial', description: '**Python** - Tutorial "Local Library" website written in Django', priority: 10 }, - { url: 'https://github.com/gitpod-io/gs-spring-boot.git', description: '**Java** - Building an Application with Spring Boot', priority: 9 }, - { url: 'https://github.com/gitpod-io/NextSimpleStarter.git', description: '**JavaScript** - Simple PWA boilerplate with Next.js and Redux', priority: 8 }, - { url: 'https://github.com/gitpod-io/go-gin-app.git', description: '**Go** - A simple web app implemented in Go and Gin', priority: 7 }, - { url: 'https://github.com/gitpod-io/rails_sample_app', description: '**Ruby on Rails** - Tutorial sample application', priority: 6 }, - { url: 'https://github.com/gitpod-io/symfony-demo.git', description: '**PHP** - Symfony Demo Application', priority: 5 }, - { url: 'https://github.com/theia-ide/theia.git', description: '**Typescript** - Deep dive into Gitpod\\\'s open-source IDE, Theia.', priority: 4 } + { + url: "https://github.com/gitpod-io/django-locallibrary-tutorial", + description: '**Python** - Tutorial "Local Library" website written in Django', + priority: 10, + }, + { + url: "https://github.com/gitpod-io/gs-spring-boot.git", + description: "**Java** - Building an Application with Spring Boot", + priority: 9, + }, + { + url: "https://github.com/gitpod-io/NextSimpleStarter.git", + description: "**JavaScript** - Simple PWA boilerplate with Next.js and Redux", + priority: 8, + }, + { + url: "https://github.com/gitpod-io/go-gin-app.git", + description: "**Go** - A simple web app implemented in Go and Gin", + priority: 7, + }, + { + url: "https://github.com/gitpod-io/rails_sample_app", + description: "**Ruby on Rails** - Tutorial sample application", + priority: 6, + }, + { + url: "https://github.com/gitpod-io/symfony-demo.git", + description: "**PHP** - Symfony Demo Application", + priority: 5, + }, + { + url: "https://github.com/theia-ide/theia.git", + description: "**Typescript** - Deep dive into Gitpod\\'s open-source IDE, Theia.", + priority: 4, + }, ]; const newEntries = [ - { url: 'https://github.com/gitpod-io/sveltejs-template', description: '**JavaScript** – A project template for Svelte applications', priority: 90 }, - { url: 'https://github.com/eclipse-theia/theia', description: '**TypeScript** – A cloud & desktop IDE framework', priority: 80 }, - { url: 'https://github.com/breatheco-de/python-flask-api-tutorial', description: '**Python** – An interactive tutorial about Python Flask', priority: 70 }, - { url: 'https://github.com/prometheus/prometheus', description: '**Go** – A monitoring system and time series database', priority: 60 }, - { url: 'https://github.com/nushell/nushell', description: '**Rust** – A terminal emulator written in Rust', priority: 50 }, - { url: 'https://github.com/gitpod-io/spring-petclinic', description: '**Java** – A Spring sample web application', priority: 40 }, - { url: 'https://github.com/gitpod-io/ruby-on-rails', description: '**Ruby** – A Rails example with PostgreSQL database', priority: 30 }, - { url: 'https://github.com/gitpod-io/dotnetcore', description: '**C#** – A simple .NET Core application example', priority: 20 }, - { url: 'https://github.com/symfony/demo', description: '**PHP** – A Symfony demo application', priority: 10 }, - ] - const currentEntries = await queryRunner.query(`SELECT * FROM d_b_repository_white_list ORDER BY priority DESC`); + { + url: "https://github.com/gitpod-io/sveltejs-template", + description: "**JavaScript** – A project template for Svelte applications", + priority: 90, + }, + { + url: "https://github.com/eclipse-theia/theia", + description: "**TypeScript** – A cloud & desktop IDE framework", + priority: 80, + }, + { + url: "https://github.com/breatheco-de/python-flask-api-tutorial", + description: "**Python** – An interactive tutorial about Python Flask", + priority: 70, + }, + { + url: "https://github.com/prometheus/prometheus", + description: "**Go** – A monitoring system and time series database", + priority: 60, + }, + { + url: "https://github.com/nushell/nushell", + description: "**Rust** – A terminal emulator written in Rust", + priority: 50, + }, + { + url: "https://github.com/gitpod-io/spring-petclinic", + description: "**Java** – A Spring sample web application", + priority: 40, + }, + { + url: "https://github.com/gitpod-io/ruby-on-rails", + description: "**Ruby** – A Rails example with PostgreSQL database", + priority: 30, + }, + { + url: "https://github.com/gitpod-io/dotnetcore", + description: "**C#** – A simple .NET Core application example", + priority: 20, + }, + { + url: "https://github.com/symfony/demo", + description: "**PHP** – A Symfony demo application", + priority: 10, + }, + ]; + const currentEntries = await queryRunner.query( + `SELECT * FROM d_b_repository_white_list ORDER BY priority DESC`, + ); if (JSON.stringify(currentEntries, null, 2) === JSON.stringify(oldEntries, null, 2)) { await queryRunner.query("DELETE FROM d_b_repository_white_list"); - await Promise.all(newEntries.map(e => queryRunner.query(`INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES (?, ?, ?)`, [e.url, e.description, e.priority]))); + await Promise.all( + newEntries.map((e) => + queryRunner.query( + `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES (?, ?, ?)`, + [e.url, e.description, e.priority], + ), + ), + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1612781029090-UserStorageUserIdChar36.ts b/components/gitpod-db/src/typeorm/migration/1612781029090-UserStorageUserIdChar36.ts index 75f3c598f8f58f..9c5d76708831cd 100644 --- a/components/gitpod-db/src/typeorm/migration/1612781029090-UserStorageUserIdChar36.ts +++ b/components/gitpod-db/src/typeorm/migration/1612781029090-UserStorageUserIdChar36.ts @@ -4,15 +4,12 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class UserStorageUserIdChar1612781029090 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("ALTER TABLE d_b_user_storage_resource MODIFY userId char(36);"); - } - - public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE d_b_user_storage_resource MODIFY userId char(36);"); } + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1613031552073-CodeSyncDB.ts b/components/gitpod-db/src/typeorm/migration/1613031552073-CodeSyncDB.ts index d9f5e532970990..1a328fe05b0267 100644 --- a/components/gitpod-db/src/typeorm/migration/1613031552073-CodeSyncDB.ts +++ b/components/gitpod-db/src/typeorm/migration/1613031552073-CodeSyncDB.ts @@ -4,13 +4,14 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { tableExists } from "./helper/helper"; export class CodeSyncDB1613031552073 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_code_sync_resource` (`userId` char(36) NOT NULL, `kind` char(64) NOT NULL, `rev` char(36) NOT NULL, `deleted` tinyint(4) NOT NULL DEFAULT 0, `created` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY(`userId`, `kind`, `rev`)) ENGINE=InnoDB"); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_code_sync_resource` (`userId` char(36) NOT NULL, `kind` char(64) NOT NULL, `rev` char(36) NOT NULL, `deleted` tinyint(4) NOT NULL DEFAULT 0, `created` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY(`userId`, `kind`, `rev`)) ENGINE=InnoDB", + ); } public async down(queryRunner: QueryRunner): Promise { @@ -18,5 +19,4 @@ export class CodeSyncDB1613031552073 implements MigrationInterface { await queryRunner.query("DROP TABLE `d_b_code_sync_resource`"); } } - } diff --git a/components/gitpod-db/src/typeorm/migration/1616920059049-UpdateExamples.ts b/components/gitpod-db/src/typeorm/migration/1616920059049-UpdateExamples.ts index 70f3f543f9849a..c8386e6eac2ffc 100644 --- a/components/gitpod-db/src/typeorm/migration/1616920059049-UpdateExamples.ts +++ b/components/gitpod-db/src/typeorm/migration/1616920059049-UpdateExamples.ts @@ -4,22 +4,43 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class UpdateExamples1616920059049 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const newEntries = [ - { url: 'https://github.com/gitpod-io/sveltejs-template', description: 'A project template for Svelte applications', priority: 90 }, - { url: 'https://github.com/gitpod-io/spring-petclinic', description: 'A Spring sample web application', priority: 70 }, - { url: 'https://github.com/breatheco-de/python-flask-api-tutorial', description: 'An interactive tutorial about Python Flask', priority: 40 }, - { url: 'https://github.com/gitpod-io/ruby-on-rails', description: 'A Rails example with PostgreSQL database', priority: 30 }, - { url: 'https://github.com/gitpod-io/dotnetcore', description: 'A simple .NET Core application example', priority: 20 }, - { url: 'https://github.com/symfony/demo', description: 'A Symfony demo application', priority: 10 }, - ] + { + url: "https://github.com/gitpod-io/sveltejs-template", + description: "A project template for Svelte applications", + priority: 90, + }, + { + url: "https://github.com/gitpod-io/spring-petclinic", + description: "A Spring sample web application", + priority: 70, + }, + { + url: "https://github.com/breatheco-de/python-flask-api-tutorial", + description: "An interactive tutorial about Python Flask", + priority: 40, + }, + { + url: "https://github.com/gitpod-io/ruby-on-rails", + description: "A Rails example with PostgreSQL database", + priority: 30, + }, + { + url: "https://github.com/gitpod-io/dotnetcore", + description: "A simple .NET Core application example", + priority: 20, + }, + { url: "https://github.com/symfony/demo", description: "A Symfony demo application", priority: 10 }, + ]; // delete old entries await queryRunner.query("DELETE FROM d_b_repository_white_list"); - const insert = `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES ${newEntries.map(e=>'(?, ?, ?)').join(', ')}`; + const insert = `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES ${newEntries + .map((e) => "(?, ?, ?)") + .join(", ")}`; const values: any[] = []; for (const e of newEntries) { values.push(e.url, e.description, e.priority); @@ -27,7 +48,5 @@ export class UpdateExamples1616920059049 implements MigrationInterface { await queryRunner.query(insert, values); } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1617205663692-WorkspaceCluster.ts b/components/gitpod-db/src/typeorm/migration/1617205663692-WorkspaceCluster.ts index 75d8b581d48e4d..867908583d4815 100644 --- a/components/gitpod-db/src/typeorm/migration/1617205663692-WorkspaceCluster.ts +++ b/components/gitpod-db/src/typeorm/migration/1617205663692-WorkspaceCluster.ts @@ -4,10 +4,9 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class WorkspaceCluster1617205663692 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` CREATE TABLE IF NOT EXISTS d_b_workspace_cluster ( @@ -21,12 +20,10 @@ export class WorkspaceCluster1617205663692 implements MigrationInterface { PRIMARY KEY (name), KEY ind_state (state)) ENGINE=InnoDB - DEFAULT CHARSET=utf8mb4;` - ); + DEFAULT CHARSET=utf8mb4;`); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.dropTable("d_b_workspace_cluster"); } - } diff --git a/components/gitpod-db/src/typeorm/migration/1617617125217-UpdateFlask.ts b/components/gitpod-db/src/typeorm/migration/1617617125217-UpdateFlask.ts index c488178848b7bb..022677cff7681f 100644 --- a/components/gitpod-db/src/typeorm/migration/1617617125217-UpdateFlask.ts +++ b/components/gitpod-db/src/typeorm/migration/1617617125217-UpdateFlask.ts @@ -4,20 +4,26 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class UpdateFlask1617617125217 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - const toDelete = [ - 'https://github.com/breatheco-de/python-flask-api-tutorial' - ]; + const toDelete = ["https://github.com/breatheco-de/python-flask-api-tutorial"]; const newEntries = [ - { url: 'https://github.com/gitpod-io/python-flask-example', description: 'The official example from Flask', priority: 40 }, - ] + { + url: "https://github.com/gitpod-io/python-flask-example", + description: "The official example from Flask", + priority: 40, + }, + ]; // delete old entries - await queryRunner.query(`DELETE FROM d_b_repository_white_list where url in (${toDelete.map(e => '?').join(', ')})`, toDelete); - const insert = `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES ${newEntries.map(e=>'(?, ?, ?)').join(', ')}`; + await queryRunner.query( + `DELETE FROM d_b_repository_white_list where url in (${toDelete.map((e) => "?").join(", ")})`, + toDelete, + ); + const insert = `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES ${newEntries + .map((e) => "(?, ?, ?)") + .join(", ")}`; const values: any[] = []; for (const e of newEntries) { values.push(e.url, e.description, e.priority); @@ -25,7 +31,5 @@ export class UpdateFlask1617617125217 implements MigrationInterface { await queryRunner.query(insert, values); } - public async down(queryRunner: QueryRunner): Promise { - } - -} \ No newline at end of file + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/components/gitpod-db/src/typeorm/migration/1620209434733-AdmissionConstraints.ts b/components/gitpod-db/src/typeorm/migration/1620209434733-AdmissionConstraints.ts index 342bbe2ebbce00..6d16d749f09f11 100644 --- a/components/gitpod-db/src/typeorm/migration/1620209434733-AdmissionConstraints.ts +++ b/components/gitpod-db/src/typeorm/migration/1620209434733-AdmissionConstraints.ts @@ -4,20 +4,19 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists, tableExists } from "./helper/helper"; export class AdmissionConstraints1620209434733 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await tableExists(queryRunner, "d_b_workspace_cluster")) { if (!(await columnExists(queryRunner, "d_b_workspace_cluster", "admissionConstraints"))) { - await queryRunner.query("ALTER TABLE d_b_workspace_cluster ADD COLUMN admissionConstraints TEXT NOT NULL"); + await queryRunner.query( + "ALTER TABLE d_b_workspace_cluster ADD COLUMN admissionConstraints TEXT NOT NULL", + ); } } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1620865190518-OAuthAuthCodeDB.ts b/components/gitpod-db/src/typeorm/migration/1620865190518-OAuthAuthCodeDB.ts index cc2d81f041ef58..f94cb9af9bb2a4 100644 --- a/components/gitpod-db/src/typeorm/migration/1620865190518-OAuthAuthCodeDB.ts +++ b/components/gitpod-db/src/typeorm/migration/1620865190518-OAuthAuthCodeDB.ts @@ -4,13 +4,14 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { tableExists } from "./helper/helper"; export class OAuthAuthCodeDB1620865190518 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_oauth_auth_code_entry` (`id` int NOT NULL AUTO_INCREMENT, `code` varchar(1024) NOT NULL, `redirectURI` varchar(1024) NOT NULL DEFAULT '', `codeChallenge` varchar(128) NOT NULL, `codeChallengeMethod` varchar(10) NOT NULL, `expiresAt` timestamp(6) NOT NULL, `userId` char(36) NOT NULL, `client` text NOT NULL, `scopes` text NOT NULL, PRIMARY KEY(`id`)) ENGINE=InnoDB;"); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_oauth_auth_code_entry` (`id` int NOT NULL AUTO_INCREMENT, `code` varchar(1024) NOT NULL, `redirectURI` varchar(1024) NOT NULL DEFAULT '', `codeChallenge` varchar(128) NOT NULL, `codeChallengeMethod` varchar(10) NOT NULL, `expiresAt` timestamp(6) NOT NULL, `userId` char(36) NOT NULL, `client` text NOT NULL, `scopes` text NOT NULL, PRIMARY KEY(`id`)) ENGINE=InnoDB;", + ); } public async down(queryRunner: QueryRunner): Promise { @@ -18,5 +19,4 @@ export class OAuthAuthCodeDB1620865190518 implements MigrationInterface { await queryRunner.query("DROP TABLE `d_b_oauth_auth_code_entry`"); } } - } diff --git a/components/gitpod-db/src/typeorm/migration/1622468446118-TeamsAndProjects.ts b/components/gitpod-db/src/typeorm/migration/1622468446118-TeamsAndProjects.ts index bb00ecd7e90ed8..1eb9b7ee241951 100644 --- a/components/gitpod-db/src/typeorm/migration/1622468446118-TeamsAndProjects.ts +++ b/components/gitpod-db/src/typeorm/migration/1622468446118-TeamsAndProjects.ts @@ -7,15 +7,19 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class TeamsAndProjects1622468446118 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_team` (`id` char(36) NOT NULL, `name` varchar(255) NOT NULL, `slug` 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;"); - 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, `role` 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_userId` (`userId`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_project` (`id` char(36) NOT NULL, `name` varchar(255) 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;"); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_team` (`id` char(36) NOT NULL, `name` varchar(255) NOT NULL, `slug` 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;", + ); + 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, `role` 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_userId` (`userId`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", + ); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_project` (`id` char(36) NOT NULL, `name` varchar(255) 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;", + ); } public async down(queryRunner: QueryRunner): Promise { // this is a one-way idempotent 'migration', no rollback possible for a nonempty DB } - } diff --git a/components/gitpod-db/src/typeorm/migration/1623652164639-TeamMembershipInvite.ts b/components/gitpod-db/src/typeorm/migration/1623652164639-TeamMembershipInvite.ts index 90c2540d51aa4b..f1c698d53f2952 100644 --- a/components/gitpod-db/src/typeorm/migration/1623652164639-TeamMembershipInvite.ts +++ b/components/gitpod-db/src/typeorm/migration/1623652164639-TeamMembershipInvite.ts @@ -4,16 +4,16 @@ * See License-AGPL.txt in the project root for license information. */ - import { MigrationInterface, QueryRunner } from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; - export class TeamsMembershipInvite1623652164639 implements MigrationInterface { +export class TeamsMembershipInvite1623652164639 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_team_membership_invite` (`id` char(36) NOT NULL, `teamId` char(36) NOT NULL, `role` varchar(255) NOT NULL, `creationTime` varchar(255) NOT NULL, `invalidationTime` varchar(255) NOT NULL DEFAULT '', `invitedEmail` varchar(255) NOT NULL DEFAULT '', `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;", + ); + } - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_team_membership_invite` (`id` char(36) NOT NULL, `teamId` char(36) NOT NULL, `role` varchar(255) NOT NULL, `creationTime` varchar(255) NOT NULL, `invalidationTime` varchar(255) NOT NULL DEFAULT '', `invitedEmail` varchar(255) NOT NULL DEFAULT '', `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;"); - } - - public async down(queryRunner: QueryRunner): Promise { - // this is a one-way idempotent 'migration', no rollback possible for a nonempty DB - } - - } + public async down(queryRunner: QueryRunner): Promise { + // this is a one-way idempotent 'migration', no rollback possible for a nonempty DB + } +} diff --git a/components/gitpod-db/src/typeorm/migration/1623652164640-UpdateExamples.ts b/components/gitpod-db/src/typeorm/migration/1623652164640-UpdateExamples.ts index 6845d8d0889bb1..ea8989df280b90 100644 --- a/components/gitpod-db/src/typeorm/migration/1623652164640-UpdateExamples.ts +++ b/components/gitpod-db/src/typeorm/migration/1623652164640-UpdateExamples.ts @@ -4,30 +4,83 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class UpdateExamples1623652164640 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { let priority = 90; const newEntries = [ - { url: 'https://github.com/gitpod-io/template-typescript-node', description: 'A Node.js app, written in TypeScript.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-typescript-react', description: 'A create-react-app template, written in TypeScript.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-python-django', description: 'A Django app template. ', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-python-flask', description: 'A Flasker app template.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/spring-petclinic', description: 'A Spring app written in Java.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-php-drupal-ddev', description: 'A Drupal app template, scaffolded by DDEV.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-php-laravel-mysql', description: 'A Laravel app template, with MySQL.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-ruby-on-rails', description: 'A Ruby on Rails app template, with Postgres. ', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-golang-cli', description: 'A CLI template, written in Go.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-rust-cli', description: 'A CLI template, written in Rust.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-dotnet-core-cli-csharp', description: 'A CLI starter for .NET written in C#.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-sveltejs', description: 'A Svelte.js app writtten in JavaScript.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-sveltejskit', description: 'A SvelteKit app template.', priority: priority-- }, - ] + { + url: "https://github.com/gitpod-io/template-typescript-node", + description: "A Node.js app, written in TypeScript.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-typescript-react", + description: "A create-react-app template, written in TypeScript.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-python-django", + description: "A Django app template. ", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-python-flask", + description: "A Flasker app template.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/spring-petclinic", + description: "A Spring app written in Java.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-php-drupal-ddev", + description: "A Drupal app template, scaffolded by DDEV.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-php-laravel-mysql", + description: "A Laravel app template, with MySQL.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-ruby-on-rails", + description: "A Ruby on Rails app template, with Postgres. ", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-golang-cli", + description: "A CLI template, written in Go.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-rust-cli", + description: "A CLI template, written in Rust.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-dotnet-core-cli-csharp", + description: "A CLI starter for .NET written in C#.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-sveltejs", + description: "A Svelte.js app writtten in JavaScript.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-sveltejskit", + description: "A SvelteKit app template.", + priority: priority--, + }, + ]; // delete old entries await queryRunner.query("DELETE FROM d_b_repository_white_list"); - const insert = `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES ${newEntries.map(e=>'(?, ?, ?)').join(', ')}`; + const insert = `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES ${newEntries + .map((e) => "(?, ?, ?)") + .join(", ")}`; const values: any[] = []; for (const e of newEntries) { values.push(e.url, e.description, e.priority); @@ -35,7 +88,5 @@ export class UpdateExamples1623652164640 implements MigrationInterface { await queryRunner.query(insert, values); } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1625458087438-UpdateExamples.ts b/components/gitpod-db/src/typeorm/migration/1625458087438-UpdateExamples.ts index 7d35d02b32adfb..426b942f042c8f 100644 --- a/components/gitpod-db/src/typeorm/migration/1625458087438-UpdateExamples.ts +++ b/components/gitpod-db/src/typeorm/migration/1625458087438-UpdateExamples.ts @@ -4,34 +4,98 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class UpdateExamples1625458087438 implements MigrationInterface { - -public async up(queryRunner: QueryRunner): Promise { + public async up(queryRunner: QueryRunner): Promise { let priority = 90; const newEntries = [ - { url: 'https://github.com/gitpod-io/template-typescript-node', description: 'A Node.js app, written in TypeScript.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-typescript-react', description: 'A create-react-app template, written in TypeScript.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-python-django', description: 'A Django app template. ', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-python-flask', description: 'A Flasker app template.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/spring-petclinic', description: 'A Spring app written in Java.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-php-drupal-ddev', description: 'A Drupal app template, scaffolded by DDEV.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-php-laravel-mysql', description: 'A Laravel app template, with MySQL.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-ruby-on-rails', description: 'A Ruby on Rails app template, with Postgres. ', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-golang-cli', description: 'A CLI template, written in Go.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-rust-cli', description: 'A CLI template, written in Rust.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-dotnet-core-cli-csharp', description: 'A CLI starter for .NET written in C#.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-sveltejs', description: 'A Svelte.js app writtten in JavaScript.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-sveltejskit', description: 'A SvelteKit app template.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-datasette', description: 'A Datasette template.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-nix', description: 'A nix template for reproducible development environments.', priority: priority-- }, - { url: 'https://github.com/gitpod-io/template-haskell', description: 'A Haskell template.', priority: priority-- }, - - ] + { + url: "https://github.com/gitpod-io/template-typescript-node", + description: "A Node.js app, written in TypeScript.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-typescript-react", + description: "A create-react-app template, written in TypeScript.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-python-django", + description: "A Django app template. ", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-python-flask", + description: "A Flasker app template.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/spring-petclinic", + description: "A Spring app written in Java.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-php-drupal-ddev", + description: "A Drupal app template, scaffolded by DDEV.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-php-laravel-mysql", + description: "A Laravel app template, with MySQL.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-ruby-on-rails", + description: "A Ruby on Rails app template, with Postgres. ", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-golang-cli", + description: "A CLI template, written in Go.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-rust-cli", + description: "A CLI template, written in Rust.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-dotnet-core-cli-csharp", + description: "A CLI starter for .NET written in C#.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-sveltejs", + description: "A Svelte.js app writtten in JavaScript.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-sveltejskit", + description: "A SvelteKit app template.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-datasette", + description: "A Datasette template.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-nix", + description: "A nix template for reproducible development environments.", + priority: priority--, + }, + { + url: "https://github.com/gitpod-io/template-haskell", + description: "A Haskell template.", + priority: priority--, + }, + ]; // delete old entries await queryRunner.query("DELETE FROM d_b_repository_white_list"); - const insert = `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES ${newEntries.map(e=>'(?, ?, ?)').join(', ')}`; + const insert = `INSERT IGNORE INTO d_b_repository_white_list (url, description, priority) VALUES ${newEntries + .map((e) => "(?, ?, ?)") + .join(", ")}`; const values: any[] = []; for (const e of newEntries) { values.push(e.url, e.description, e.priority); @@ -39,7 +103,5 @@ public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(insert, values); } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1626351957505-AddProjectIdToPrebuiltWorkspace.ts b/components/gitpod-db/src/typeorm/migration/1626351957505-AddProjectIdToPrebuiltWorkspace.ts index 37cd91913ad3cb..62624de3780850 100644 --- a/components/gitpod-db/src/typeorm/migration/1626351957505-AddProjectIdToPrebuiltWorkspace.ts +++ b/components/gitpod-db/src/typeorm/migration/1626351957505-AddProjectIdToPrebuiltWorkspace.ts @@ -4,15 +4,16 @@ * See License-AGPL.txt in the project root for license information. */ - import {MigrationInterface, QueryRunner} from "typeorm"; - import { tableExists, columnExists, indexExists } from "./helper/helper"; +import { MigrationInterface, QueryRunner } from "typeorm"; +import { tableExists, columnExists, indexExists } from "./helper/helper"; export class AddProjectIdToPrebuiltWorkspace1626351957505 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await tableExists(queryRunner, "d_b_prebuilt_workspace")) { if (!(await columnExists(queryRunner, "d_b_prebuilt_workspace", "projectId"))) { - await queryRunner.query("ALTER TABLE d_b_prebuilt_workspace ADD COLUMN `projectId` char(36) DEFAULT NULL, ADD COLUMN `branch` varchar(255) DEFAULT NULL"); + await queryRunner.query( + "ALTER TABLE d_b_prebuilt_workspace ADD COLUMN `projectId` char(36) DEFAULT NULL, ADD COLUMN `branch` varchar(255) DEFAULT NULL", + ); } } if (!(await indexExists(queryRunner, "d_b_project", "cloneUrl"))) { @@ -20,7 +21,5 @@ export class AddProjectIdToPrebuiltWorkspace1626351957505 implements MigrationIn } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1626422169862-AddProjectIdToWorkspace.ts b/components/gitpod-db/src/typeorm/migration/1626422169862-AddProjectIdToWorkspace.ts index 3db5b8acf8ed51..9ee72035d49ff1 100644 --- a/components/gitpod-db/src/typeorm/migration/1626422169862-AddProjectIdToWorkspace.ts +++ b/components/gitpod-db/src/typeorm/migration/1626422169862-AddProjectIdToWorkspace.ts @@ -8,7 +8,6 @@ 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"))) { @@ -17,7 +16,5 @@ export class AddProjectIdToWorkspace1626422169862 implements MigrationInterface } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1626784647462-WorkspaceInstanceStoppingTime.ts b/components/gitpod-db/src/typeorm/migration/1626784647462-WorkspaceInstanceStoppingTime.ts index f97cc4394529e8..8ab9e85d519933 100644 --- a/components/gitpod-db/src/typeorm/migration/1626784647462-WorkspaceInstanceStoppingTime.ts +++ b/components/gitpod-db/src/typeorm/migration/1626784647462-WorkspaceInstanceStoppingTime.ts @@ -4,20 +4,19 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists, tableExists } from "./helper/helper"; export class WorkspaceInstanceStoppingTime1626784647462 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await tableExists(queryRunner, "d_b_workspace_instance")) { if (!(await columnExists(queryRunner, "d_b_workspace_instance", "stoppingTime"))) { - await queryRunner.query("ALTER TABLE d_b_workspace_instance ADD COLUMN stoppingTime varchar(255) NOT NULL DEFAULT ''"); + await queryRunner.query( + "ALTER TABLE d_b_workspace_instance ADD COLUMN stoppingTime varchar(255) NOT NULL DEFAULT ''", + ); } } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1626853418369-ProjectConfiguration.ts b/components/gitpod-db/src/typeorm/migration/1626853418369-ProjectConfiguration.ts index f3b54882ec793d..9cc9ec7a30f5bf 100644 --- a/components/gitpod-db/src/typeorm/migration/1626853418369-ProjectConfiguration.ts +++ b/components/gitpod-db/src/typeorm/migration/1626853418369-ProjectConfiguration.ts @@ -8,14 +8,11 @@ import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class ProjectConfiguration1626853418369 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_project", "config"))) { await queryRunner.query("ALTER TABLE d_b_project ADD COLUMN config text"); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1626881279438-AddMarkedDeletedToProject.ts b/components/gitpod-db/src/typeorm/migration/1626881279438-AddMarkedDeletedToProject.ts index ea8c69b4531942..cfa47d1ce57def 100644 --- a/components/gitpod-db/src/typeorm/migration/1626881279438-AddMarkedDeletedToProject.ts +++ b/components/gitpod-db/src/typeorm/migration/1626881279438-AddMarkedDeletedToProject.ts @@ -4,18 +4,17 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class AddMarkedDeletedToProject1626881279438 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_project", "markedDeleted"))) { - await queryRunner.query("ALTER TABLE d_b_project ADD COLUMN `markedDeleted` tinyint(4) NOT NULL DEFAULT '0'"); + await queryRunner.query( + "ALTER TABLE d_b_project ADD COLUMN `markedDeleted` tinyint(4) NOT NULL DEFAULT '0'", + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1627287775965-UserProjects.ts b/components/gitpod-db/src/typeorm/migration/1627287775965-UserProjects.ts index a54249703e152e..b570631c12b1df 100644 --- a/components/gitpod-db/src/typeorm/migration/1627287775965-UserProjects.ts +++ b/components/gitpod-db/src/typeorm/migration/1627287775965-UserProjects.ts @@ -8,7 +8,6 @@ import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class UserProjects1627287775965 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_project", "userId"))) { await queryRunner.query("ALTER TABLE d_b_project MODIFY COLUMN `teamId` char(36) NULL"); @@ -17,7 +16,5 @@ export class UserProjects1627287775965 implements MigrationInterface { } } - public async down(queryRunner: QueryRunner): Promise { - } - -} \ No newline at end of file + public async down(queryRunner: QueryRunner): Promise {} +} diff --git a/components/gitpod-db/src/typeorm/migration/1628674695873-DeprecateAllowsMarketingCommunication.ts b/components/gitpod-db/src/typeorm/migration/1628674695873-DeprecateAllowsMarketingCommunication.ts index 14200d16c2fce4..94f3ef5cfa2b6a 100644 --- a/components/gitpod-db/src/typeorm/migration/1628674695873-DeprecateAllowsMarketingCommunication.ts +++ b/components/gitpod-db/src/typeorm/migration/1628674695873-DeprecateAllowsMarketingCommunication.ts @@ -4,22 +4,27 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class DeprecateAllowsMarketingCommunication1628674695873 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await columnExists(queryRunner, "d_b_user", "allowsMarketingCommunication")) { - await queryRunner.query("UPDATE d_b_user set additionalData = JSON_MERGE_PATCH(IFNULL(additionalData, '{}'), JSON_SET('{\"emailNotificationSettings\":{\"allowsChangelogMail\":true}}', '$.emailNotificationSettings.allowsChangelogMail', IF(allowsMarketingCommunication, 'true', 'false')))"); + await queryRunner.query( + "UPDATE d_b_user set additionalData = JSON_MERGE_PATCH(IFNULL(additionalData, '{}'), JSON_SET('{\"emailNotificationSettings\":{\"allowsChangelogMail\":true}}', '$.emailNotificationSettings.allowsChangelogMail', IF(allowsMarketingCommunication, 'true', 'false')))", + ); await queryRunner.query("ALTER TABLE d_b_user DROP COLUMN allowsMarketingCommunication"); } } public async down(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_user", "allowsMarketingCommunication"))) { - await queryRunner.query("ALTER TABLE d_b_user ADD COLUMN allowsMarketingCommunication tinyint(4) NOT NULL DEFAULT '0'"); - await queryRunner.query("UPDATE d_b_user set allowsMarketingCommunication = IF(additionalData->>'$.emailNotificationSettings.allowsChangelogMail'='true', 1, 0)"); + await queryRunner.query( + "ALTER TABLE d_b_user ADD COLUMN allowsMarketingCommunication tinyint(4) NOT NULL DEFAULT '0'", + ); + await queryRunner.query( + "UPDATE d_b_user set allowsMarketingCommunication = IF(additionalData->>'$.emailNotificationSettings.allowsChangelogMail'='true', 1, 0)", + ); } } } diff --git a/components/gitpod-db/src/typeorm/migration/1629706076011-FixCreationTimeColumns.ts b/components/gitpod-db/src/typeorm/migration/1629706076011-FixCreationTimeColumns.ts index 57bad5fb02927d..9bda7d33a353cd 100644 --- a/components/gitpod-db/src/typeorm/migration/1629706076011-FixCreationTimeColumns.ts +++ b/components/gitpod-db/src/typeorm/migration/1629706076011-FixCreationTimeColumns.ts @@ -4,17 +4,20 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class FixCreationTimeColumns1629706076011 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("ALTER TABLE d_b_app_installation MODIFY creationTime timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6);"); - await queryRunner.query("ALTER TABLE d_b_prebuilt_workspace MODIFY creationTime timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6);"); - await queryRunner.query("ALTER TABLE d_b_snapshot MODIFY creationTime timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6);"); - } - - public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE d_b_app_installation MODIFY creationTime timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6);", + ); + await queryRunner.query( + "ALTER TABLE d_b_prebuilt_workspace MODIFY creationTime timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6);", + ); + await queryRunner.query( + "ALTER TABLE d_b_snapshot MODIFY creationTime timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6);", + ); } + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1630566926740-AddPrebuildInfo.ts b/components/gitpod-db/src/typeorm/migration/1630566926740-AddPrebuildInfo.ts index a2c95aa3e95c4f..1973604c06e3c5 100644 --- a/components/gitpod-db/src/typeorm/migration/1630566926740-AddPrebuildInfo.ts +++ b/components/gitpod-db/src/typeorm/migration/1630566926740-AddPrebuildInfo.ts @@ -4,15 +4,14 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class AddPrebuildInfo1630566926740 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_prebuild_info` ( `prebuildId` char(36) NOT NULL, `info` text 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 (`prebuildId`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - } - - public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_prebuild_info` ( `prebuildId` char(36) NOT NULL, `info` text 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 (`prebuildId`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", + ); } + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1630586785018-UpdateDevxAndOnboardingMail.ts b/components/gitpod-db/src/typeorm/migration/1630586785018-UpdateDevxAndOnboardingMail.ts index 36181e062d39a5..13be5177a99d9b 100644 --- a/components/gitpod-db/src/typeorm/migration/1630586785018-UpdateDevxAndOnboardingMail.ts +++ b/components/gitpod-db/src/typeorm/migration/1630586785018-UpdateDevxAndOnboardingMail.ts @@ -4,19 +4,20 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class UpdateDevxAndOnboardingMail1630586785018 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await columnExists(queryRunner, "d_b_user", "additionalData")) { - await queryRunner.query("UPDATE d_b_user set additionalData = json_set(additionalData, '$.emailNotificationSettings.allowsDevXMail', true) WHERE (json_extract(additionalData, '$.emailNotificationSettings.allowsChangelogMail')=true OR json_extract(additionalData, '$.emailNotificationSettings.allowsChangelogMail')='true') AND json_extract(additionalData, '$.emailNotificationSettings.allowsDevXMail') IS NULL") - await queryRunner.query("UPDATE d_b_user set additionalData = json_set(additionalData, '$.emailNotificationSettings.allowsOnboardingMail', true) WHERE json_extract(additionalData, '$.emailNotificationSettings.allowsOnboardingMail') IS NULL") + await queryRunner.query( + "UPDATE d_b_user set additionalData = json_set(additionalData, '$.emailNotificationSettings.allowsDevXMail', true) WHERE (json_extract(additionalData, '$.emailNotificationSettings.allowsChangelogMail')=true OR json_extract(additionalData, '$.emailNotificationSettings.allowsChangelogMail')='true') AND json_extract(additionalData, '$.emailNotificationSettings.allowsDevXMail') IS NULL", + ); + await queryRunner.query( + "UPDATE d_b_user set additionalData = json_set(additionalData, '$.emailNotificationSettings.allowsOnboardingMail', true) WHERE json_extract(additionalData, '$.emailNotificationSettings.allowsOnboardingMail') IS NULL", + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1632224603256-ReplaceStringBooleansWithBooleans.ts b/components/gitpod-db/src/typeorm/migration/1632224603256-ReplaceStringBooleansWithBooleans.ts index 03545a3e2b13a2..0757c7f60f13f2 100644 --- a/components/gitpod-db/src/typeorm/migration/1632224603256-ReplaceStringBooleansWithBooleans.ts +++ b/components/gitpod-db/src/typeorm/migration/1632224603256-ReplaceStringBooleansWithBooleans.ts @@ -4,19 +4,20 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class ReplaceStringBooleansWithBooleans1632224603256 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await columnExists(queryRunner, "d_b_user", "additionalData")) { - await queryRunner.query("update d_b_user set additionalData = json_set(additionalData, '$.emailNotificationSettings.allowsChangelogMail', true) where json_extract(additionalData, '$.emailNotificationSettings.allowsChangelogMail') = 'true'"); - await queryRunner.query("update d_b_user set additionalData = json_set(additionalData, '$.emailNotificationSettings.allowsChangelogMail', false) where json_extract(additionalData, '$.emailNotificationSettings.allowsChangelogMail') = 'false'") + await queryRunner.query( + "update d_b_user set additionalData = json_set(additionalData, '$.emailNotificationSettings.allowsChangelogMail', true) where json_extract(additionalData, '$.emailNotificationSettings.allowsChangelogMail') = 'true'", + ); + await queryRunner.query( + "update d_b_user set additionalData = json_set(additionalData, '$.emailNotificationSettings.allowsChangelogMail', false) where json_extract(additionalData, '$.emailNotificationSettings.allowsChangelogMail') = 'false'", + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1632903852031-IndexBuildWorkspaceId.ts b/components/gitpod-db/src/typeorm/migration/1632903852031-IndexBuildWorkspaceId.ts index ec9cbd58b8412b..6453dea707c1a6 100644 --- a/components/gitpod-db/src/typeorm/migration/1632903852031-IndexBuildWorkspaceId.ts +++ b/components/gitpod-db/src/typeorm/migration/1632903852031-IndexBuildWorkspaceId.ts @@ -4,14 +4,13 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; const INDEX_NAME = "ind_buildWorkspaceId"; const TABLE_NAME = "d_b_prebuilt_workspace"; export class IndexBuildWorkspaceId1632903852031 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await indexExists(queryRunner, TABLE_NAME, INDEX_NAME))) { await queryRunner.query(`CREATE INDEX ${INDEX_NAME} ON ${TABLE_NAME} (buildWorkspaceId)`); @@ -21,5 +20,4 @@ export class IndexBuildWorkspaceId1632903852031 implements MigrationInterface { public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`DROP INDEX ${INDEX_NAME} ON ${TABLE_NAME}`); } - } diff --git a/components/gitpod-db/src/typeorm/migration/1632908105486-AddSoftDeletedToTeam.ts b/components/gitpod-db/src/typeorm/migration/1632908105486-AddSoftDeletedToTeam.ts index 5724c9e345f727..dc1a2d4994ec34 100644 --- a/components/gitpod-db/src/typeorm/migration/1632908105486-AddSoftDeletedToTeam.ts +++ b/components/gitpod-db/src/typeorm/migration/1632908105486-AddSoftDeletedToTeam.ts @@ -4,18 +4,15 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class AddMarkedDeletedToTeam1632908105486 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_team", "markedDeleted"))) { await queryRunner.query("ALTER TABLE d_b_team ADD COLUMN `markedDeleted` tinyint(4) NOT NULL DEFAULT '0'"); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1633334251143-IndexCodeSyncCreated.ts b/components/gitpod-db/src/typeorm/migration/1633334251143-IndexCodeSyncCreated.ts index 0b307f30fa83e5..349a99bf361c50 100644 --- a/components/gitpod-db/src/typeorm/migration/1633334251143-IndexCodeSyncCreated.ts +++ b/components/gitpod-db/src/typeorm/migration/1633334251143-IndexCodeSyncCreated.ts @@ -4,14 +4,13 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; const INDEX_NAME = "ind_dbsync"; const TABLE_NAME = "d_b_code_sync_resource"; export class IndexCodeSyncCreated1633334251143 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await indexExists(queryRunner, TABLE_NAME, INDEX_NAME))) { await queryRunner.query(`CREATE INDEX ${INDEX_NAME} ON ${TABLE_NAME} (created)`); @@ -21,5 +20,4 @@ export class IndexCodeSyncCreated1633334251143 implements MigrationInterface { public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`DROP INDEX ${INDEX_NAME} ON ${TABLE_NAME}`); } - } diff --git a/components/gitpod-db/src/typeorm/migration/1633989579520-AdmissionPreference.ts b/components/gitpod-db/src/typeorm/migration/1633989579520-AdmissionPreference.ts index 8f24b7b195856b..c07f3a70412cbf 100644 --- a/components/gitpod-db/src/typeorm/migration/1633989579520-AdmissionPreference.ts +++ b/components/gitpod-db/src/typeorm/migration/1633989579520-AdmissionPreference.ts @@ -4,11 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists, tableExists } from "./helper/helper"; export class AdmissionPreference1633989579520 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await tableExists(queryRunner, "d_b_workspace_cluster")) { if (!(await columnExists(queryRunner, "d_b_workspace_cluster", "admissionPreferences"))) { @@ -17,7 +16,5 @@ export class AdmissionPreference1633989579520 implements MigrationInterface { } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1634894637934-AddSlugToProject.ts b/components/gitpod-db/src/typeorm/migration/1634894637934-AddSlugToProject.ts index adb45c4e791237..8b810d37b5ab99 100644 --- a/components/gitpod-db/src/typeorm/migration/1634894637934-AddSlugToProject.ts +++ b/components/gitpod-db/src/typeorm/migration/1634894637934-AddSlugToProject.ts @@ -4,21 +4,20 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class AddSlugToProject1634894637934 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_project", "slug"))) { await queryRunner.query("ALTER TABLE d_b_project ADD COLUMN `slug` varchar(255) NULL"); } if (await columnExists(queryRunner, "d_b_project", "slug")) { - await queryRunner.query("UPDATE d_b_project SET slug = name WHERE cloneUrl LIKE '%github%' AND slug IS NULL;"); + await queryRunner.query( + "UPDATE d_b_project SET slug = name WHERE cloneUrl LIKE '%github%' AND slug IS NULL;", + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1635953505010-SnapshotState.ts b/components/gitpod-db/src/typeorm/migration/1635953505010-SnapshotState.ts index 11e500e99b0520..6f9dd56418fd98 100644 --- a/components/gitpod-db/src/typeorm/migration/1635953505010-SnapshotState.ts +++ b/components/gitpod-db/src/typeorm/migration/1635953505010-SnapshotState.ts @@ -4,17 +4,18 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists, indexExists } from "./helper/helper"; const TABLE_NAME = "d_b_snapshot"; const INDEX_NAME = "ind_state"; export class SnapshotState1635953505010 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, TABLE_NAME, "availableTime"))) { - await queryRunner.query(`ALTER TABLE ${TABLE_NAME} ADD COLUMN availableTime varchar(255) NOT NULL DEFAULT ''`); + await queryRunner.query( + `ALTER TABLE ${TABLE_NAME} ADD COLUMN availableTime varchar(255) NOT NULL DEFAULT ''`, + ); } if (!(await columnExists(queryRunner, TABLE_NAME, "state"))) { await queryRunner.query(`ALTER TABLE ${TABLE_NAME} ADD COLUMN state char(32) NOT NULL DEFAULT 'available'`); @@ -36,5 +37,4 @@ export class SnapshotState1635953505010 implements MigrationInterface { await queryRunner.query(`ALTER TABLE ${TABLE_NAME} DROP COLUMN message`); await queryRunner.query(`DROP INDEX ${INDEX_NAME} ON ${TABLE_NAME}`); } - } diff --git a/components/gitpod-db/src/typeorm/migration/1637586292251-IndexTokenEntryExpiryDate.ts b/components/gitpod-db/src/typeorm/migration/1637586292251-IndexTokenEntryExpiryDate.ts index cf746506090fe1..8b3674836ccf66 100644 --- a/components/gitpod-db/src/typeorm/migration/1637586292251-IndexTokenEntryExpiryDate.ts +++ b/components/gitpod-db/src/typeorm/migration/1637586292251-IndexTokenEntryExpiryDate.ts @@ -4,11 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; export class IndexTokenEntryExpiryDate1637586292251 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const TABLE_NAME = "d_b_token_entry"; const INDEX_NAME = "ind_expiryDate"; @@ -18,7 +17,5 @@ export class IndexTokenEntryExpiryDate1637586292251 implements MigrationInterfac } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1637586576910-IndexSubscriptionTsSlotId.ts b/components/gitpod-db/src/typeorm/migration/1637586576910-IndexSubscriptionTsSlotId.ts index 893e8ddc5f404a..9a0b1e2680d1b2 100644 --- a/components/gitpod-db/src/typeorm/migration/1637586576910-IndexSubscriptionTsSlotId.ts +++ b/components/gitpod-db/src/typeorm/migration/1637586576910-IndexSubscriptionTsSlotId.ts @@ -4,11 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; export class IndexSubscriptionTsSlotId1637586576910 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const TABLE_NAME = "d_b_subscription"; const INDEX_NAME = "ind_teamSubscriptionSlotId"; @@ -18,7 +17,5 @@ export class IndexSubscriptionTsSlotId1637586576910 implements MigrationInterfac } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1637587163581-IndexPWSProjectId.ts b/components/gitpod-db/src/typeorm/migration/1637587163581-IndexPWSProjectId.ts index 78e57625599fb3..d00ca05b5c76b6 100644 --- a/components/gitpod-db/src/typeorm/migration/1637587163581-IndexPWSProjectId.ts +++ b/components/gitpod-db/src/typeorm/migration/1637587163581-IndexPWSProjectId.ts @@ -4,11 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; export class IndexPWSProjectId1637587163581 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const TABLE_NAME = "d_b_prebuilt_workspace"; const INDEX_NAME = "ind_projectId"; @@ -18,7 +17,5 @@ export class IndexPWSProjectId1637587163581 implements MigrationInterface { } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1637587650752-IndexWorkspaceGCQueries.ts b/components/gitpod-db/src/typeorm/migration/1637587650752-IndexWorkspaceGCQueries.ts index fbfbd6cc28e3fa..b8b834e2dad1ac 100644 --- a/components/gitpod-db/src/typeorm/migration/1637587650752-IndexWorkspaceGCQueries.ts +++ b/components/gitpod-db/src/typeorm/migration/1637587650752-IndexWorkspaceGCQueries.ts @@ -4,11 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; export class IndexWorkspaceGCQueries1637587650752 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const TABLE_NAME = "d_b_workspace"; const CREATION_TIME_INDEX_NAME = "ind_creationTime"; @@ -18,16 +17,18 @@ export class IndexWorkspaceGCQueries1637587650752 implements MigrationInterface const CONTENT_DELETION_INDEX_NAME = "ind_contentDeletion"; if (!(await indexExists(queryRunner, TABLE_NAME, CONTENT_DELETION_INDEX_NAME))) { - await queryRunner.query(`CREATE INDEX ${CONTENT_DELETION_INDEX_NAME} ON ${TABLE_NAME} (contentDeletedTime, creationTime)`); + await queryRunner.query( + `CREATE INDEX ${CONTENT_DELETION_INDEX_NAME} ON ${TABLE_NAME} (contentDeletedTime, creationTime)`, + ); } const SOFT_DELETION_INDEX_NAME = "ind_softDeletion"; if (!(await indexExists(queryRunner, TABLE_NAME, SOFT_DELETION_INDEX_NAME))) { - await queryRunner.query(`CREATE INDEX ${SOFT_DELETION_INDEX_NAME} ON ${TABLE_NAME} (softDeletedTime, softDeleted)`); + await queryRunner.query( + `CREATE INDEX ${SOFT_DELETION_INDEX_NAME} ON ${TABLE_NAME} (softDeletedTime, softDeleted)`, + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1638025268018-ProjectSettings.ts b/components/gitpod-db/src/typeorm/migration/1638025268018-ProjectSettings.ts index 4882bfd20c98b8..361bda808645ef 100644 --- a/components/gitpod-db/src/typeorm/migration/1638025268018-ProjectSettings.ts +++ b/components/gitpod-db/src/typeorm/migration/1638025268018-ProjectSettings.ts @@ -8,14 +8,11 @@ import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class ProjectSettings1638025268018 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_project", "settings"))) { await queryRunner.query("ALTER TABLE d_b_project ADD COLUMN `settings` text NULL"); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1638973332787-OssAllowlist.ts b/components/gitpod-db/src/typeorm/migration/1638973332787-OssAllowlist.ts index c1fb7031f99737..27f7732c872961 100644 --- a/components/gitpod-db/src/typeorm/migration/1638973332787-OssAllowlist.ts +++ b/components/gitpod-db/src/typeorm/migration/1638973332787-OssAllowlist.ts @@ -4,13 +4,14 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { tableExists } from "./helper/helper"; export class OssAllowList1638973332787 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_oss_allow_list` (`identity` char(128) 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(`identity`)) ENGINE=InnoDB"); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_oss_allow_list` (`identity` char(128) 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(`identity`)) ENGINE=InnoDB", + ); } public async down(queryRunner: QueryRunner): Promise { @@ -18,5 +19,4 @@ export class OssAllowList1638973332787 implements MigrationInterface { await queryRunner.query("DROP TABLE `d_b_oss_allow_list`"); } } - } diff --git a/components/gitpod-db/src/typeorm/migration/1641287965022-DropDBPaymentSourceInfo.ts b/components/gitpod-db/src/typeorm/migration/1641287965022-DropDBPaymentSourceInfo.ts index a043b1b3018321..8f1d1b74de6f0d 100644 --- a/components/gitpod-db/src/typeorm/migration/1641287965022-DropDBPaymentSourceInfo.ts +++ b/components/gitpod-db/src/typeorm/migration/1641287965022-DropDBPaymentSourceInfo.ts @@ -4,15 +4,12 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; export class DropDBPaymentSourceInfo1641287965022 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { queryRunner.query("DROP TABLE `d_b_payment_source_info`"); } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1642422506330-InstallationAdminTable.ts b/components/gitpod-db/src/typeorm/migration/1642422506330-InstallationAdminTable.ts index d8def4c848a93b..1bea937eab888c 100644 --- a/components/gitpod-db/src/typeorm/migration/1642422506330-InstallationAdminTable.ts +++ b/components/gitpod-db/src/typeorm/migration/1642422506330-InstallationAdminTable.ts @@ -8,10 +8,11 @@ import { MigrationInterface, QueryRunner } from "typeorm"; import { tableExists } from "./helper/helper"; export class InstallationAdminTable1642422506330 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await tableExists(queryRunner, "d_b_installation_admin"))) { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_installation_admin` (`id` char(128) NOT NULL, `settings` text NULL, `_lastModified` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY(`id`)) ENGINE=InnoDB"); + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_installation_admin` (`id` char(128) NOT NULL, `settings` text NULL, `_lastModified` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY(`id`)) ENGINE=InnoDB", + ); } } @@ -20,5 +21,4 @@ export class InstallationAdminTable1642422506330 implements MigrationInterface { await queryRunner.query("DROP TABLE `d_b_installation_admin`"); } } - } diff --git a/components/gitpod-db/src/typeorm/migration/1642493449937-ProjectEnvVars.ts b/components/gitpod-db/src/typeorm/migration/1642493449937-ProjectEnvVars.ts index 4a52ac0eccf197..70a28acda802d7 100644 --- a/components/gitpod-db/src/typeorm/migration/1642493449937-ProjectEnvVars.ts +++ b/components/gitpod-db/src/typeorm/migration/1642493449937-ProjectEnvVars.ts @@ -7,12 +7,11 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class ProjectEnvVars1642493449937 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_project_env_var` (`id` char(36) NOT NULL, `projectId` char(36) NOT NULL, `name` varchar(255) NOT NULL, `value` text NOT NULL, `censored` tinyint(4) 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`, `projectId`), KEY `ind_projectid` (projectId), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - } - - public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_project_env_var` (`id` char(36) NOT NULL, `projectId` char(36) NOT NULL, `name` varchar(255) NOT NULL, `value` text NOT NULL, `censored` tinyint(4) 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`, `projectId`), KEY `ind_projectid` (projectId), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", + ); } + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1642497869312-ProjectInfo.ts b/components/gitpod-db/src/typeorm/migration/1642497869312-ProjectInfo.ts index 93965194ff0aa3..cfb3b294d2f0a0 100644 --- a/components/gitpod-db/src/typeorm/migration/1642497869312-ProjectInfo.ts +++ b/components/gitpod-db/src/typeorm/migration/1642497869312-ProjectInfo.ts @@ -7,12 +7,11 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class ProjectInfo1642497869312 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query("CREATE TABLE IF NOT EXISTS `d_b_project_info` ( `projectId` char(36) NOT NULL, `overview` longtext 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 (`projectId`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); - } - - public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "CREATE TABLE IF NOT EXISTS `d_b_project_info` ( `projectId` char(36) NOT NULL, `overview` longtext 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 (`projectId`), KEY `ind_dbsync` (`_lastModified`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", + ); } + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1643589063084-IndexProjectCloneUrl.ts b/components/gitpod-db/src/typeorm/migration/1643589063084-IndexProjectCloneUrl.ts index 78a06c0e667d13..3e8e9f25a16da7 100644 --- a/components/gitpod-db/src/typeorm/migration/1643589063084-IndexProjectCloneUrl.ts +++ b/components/gitpod-db/src/typeorm/migration/1643589063084-IndexProjectCloneUrl.ts @@ -4,21 +4,18 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; export class IndexProjectCloneUrl1643589063084 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const TABLE_NAME = "d_b_project"; const INDEX_NAME = "ind_cloneUrl"; - if(!(await indexExists(queryRunner, TABLE_NAME, INDEX_NAME))) { + if (!(await indexExists(queryRunner, TABLE_NAME, INDEX_NAME))) { await queryRunner.query(`CREATE INDEX ${INDEX_NAME} ON ${TABLE_NAME} (cloneUrl)`); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1643724132624-ImageBuildInfo.ts b/components/gitpod-db/src/typeorm/migration/1643724132624-ImageBuildInfo.ts index 7edcef68b230b8..a27050dcb7b452 100644 --- a/components/gitpod-db/src/typeorm/migration/1643724132624-ImageBuildInfo.ts +++ b/components/gitpod-db/src/typeorm/migration/1643724132624-ImageBuildInfo.ts @@ -4,18 +4,15 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class ImageBuildInfo1643724132624 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_workspace_instance", "imageBuildInfo"))) { await queryRunner.query("ALTER TABLE d_b_workspace_instance ADD COLUMN `imageBuildInfo` text NULL"); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1643724196672-RemoveAdmissionPreferences.ts b/components/gitpod-db/src/typeorm/migration/1643724196672-RemoveAdmissionPreferences.ts index 2a39e72080b29a..cdf05802f496ee 100644 --- a/components/gitpod-db/src/typeorm/migration/1643724196672-RemoveAdmissionPreferences.ts +++ b/components/gitpod-db/src/typeorm/migration/1643724196672-RemoveAdmissionPreferences.ts @@ -4,11 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists, tableExists } from "./helper/helper"; export class RemoveAdmissionPreferences1643724196672 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (await tableExists(queryRunner, "d_b_workspace_cluster")) { if (!(await columnExists(queryRunner, "d_b_workspace_cluster", "admissionPreferences"))) { @@ -17,7 +16,5 @@ export class RemoveAdmissionPreferences1643724196672 implements MigrationInterfa } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1643879235654-PWSUIndexes.ts b/components/gitpod-db/src/typeorm/migration/1643879235654-PWSUIndexes.ts index f0baf19b427fe8..df722bb6ca999b 100644 --- a/components/gitpod-db/src/typeorm/migration/1643879235654-PWSUIndexes.ts +++ b/components/gitpod-db/src/typeorm/migration/1643879235654-PWSUIndexes.ts @@ -4,21 +4,18 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; export class PWSUIndexes1643879235654 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { const TABLE_NAME = "d_b_prebuilt_workspace_updatable"; const INDEX_NAME = "ind_prebuiltWorkspaceId_isResolved"; - if(!(await indexExists(queryRunner, TABLE_NAME, INDEX_NAME))) { + if (!(await indexExists(queryRunner, TABLE_NAME, INDEX_NAME))) { await queryRunner.query(`CREATE INDEX ${INDEX_NAME} ON ${TABLE_NAME} (prebuiltWorkspaceId, isResolved)`); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1643986994402-OAuthRevision.ts b/components/gitpod-db/src/typeorm/migration/1643986994402-OAuthRevision.ts index 0c8d057cd6276f..f6a87af600c854 100644 --- a/components/gitpod-db/src/typeorm/migration/1643986994402-OAuthRevision.ts +++ b/components/gitpod-db/src/typeorm/migration/1643986994402-OAuthRevision.ts @@ -8,7 +8,7 @@ import { AuthProviderEntry } from "@gitpod/gitpod-protocol"; import { MigrationInterface, QueryRunner } from "typeorm"; import { dbContainerModule } from "../../container-module"; import { columnExists, indexExists } from "./helper/helper"; -import { Container } from 'inversify'; +import { Container } from "inversify"; import { AuthProviderEntryDB } from "../../auth-provider-entry-db"; import { UserDB } from "../../user-db"; @@ -17,11 +17,12 @@ const COLUMN_NAME: keyof AuthProviderEntry = "oauthRevision"; const INDEX_NAME = "ind_oauthRevision"; export class OAuthRevision1643986994402 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { // create new column if (!(await columnExists(queryRunner, TABLE_NAME, COLUMN_NAME))) { - await queryRunner.query(`ALTER TABLE ${TABLE_NAME} ADD COLUMN ${COLUMN_NAME} varchar(128) NOT NULL DEFAULT ''`); + await queryRunner.query( + `ALTER TABLE ${TABLE_NAME} ADD COLUMN ${COLUMN_NAME} varchar(128) NOT NULL DEFAULT ''`, + ); } // create index on said column @@ -34,7 +35,7 @@ export class OAuthRevision1643986994402 implements MigrationInterface { const container = new Container(); container.load(dbContainerModule); - container.get(UserDB); // initializes encryptionProvider as side effect + container.get(UserDB); // initializes encryptionProvider as side effect const db = container.get(AuthProviderEntryDB); const allProviders = await db.findAll([]); const writes: Promise[] = []; @@ -48,5 +49,4 @@ export class OAuthRevision1643986994402 implements MigrationInterface { await queryRunner.query(`ALTER TABLE ${TABLE_NAME} DROP INDEX ${INDEX_NAME}`); await queryRunner.query(`ALTER TABLE ${TABLE_NAME} DROP COLUMN ${COLUMN_NAME}`); } - } diff --git a/components/gitpod-db/src/typeorm/migration/1645019483643-InstancesByPhaseAndRegion.ts b/components/gitpod-db/src/typeorm/migration/1645019483643-InstancesByPhaseAndRegion.ts index 3fdcf69630d389..fa1e65000072a2 100644 --- a/components/gitpod-db/src/typeorm/migration/1645019483643-InstancesByPhaseAndRegion.ts +++ b/components/gitpod-db/src/typeorm/migration/1645019483643-InstancesByPhaseAndRegion.ts @@ -4,21 +4,18 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; const TABLE_NAME = "d_b_workspace_instance"; const INDEX_NAME = "ind_phasePersisted_region"; export class InstancesByPhaseAndRegion1645019483643 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - if(!(await indexExists(queryRunner, TABLE_NAME, INDEX_NAME))) { + if (!(await indexExists(queryRunner, TABLE_NAME, INDEX_NAME))) { await queryRunner.query(`CREATE INDEX ${INDEX_NAME} ON ${TABLE_NAME} (phasePersisted, region)`); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1646410374581-TSNoMoreResources.ts b/components/gitpod-db/src/typeorm/migration/1646410374581-TSNoMoreResources.ts index e891b77e657e6f..06f53365775990 100644 --- a/components/gitpod-db/src/typeorm/migration/1646410374581-TSNoMoreResources.ts +++ b/components/gitpod-db/src/typeorm/migration/1646410374581-TSNoMoreResources.ts @@ -4,21 +4,20 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; const TABLE_NAME = "d_b_team_subscription"; const COLUMN_NAME = "excludeFromMoreResources"; export class TSExcludeFromMoreResources1646410374581 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, TABLE_NAME, COLUMN_NAME))) { - await queryRunner.query(`ALTER TABLE ${TABLE_NAME} ADD COLUMN ${COLUMN_NAME} tinyint(4) NOT NULL DEFAULT '0', ALGORITHM=INPLACE, LOCK=NONE `); + await queryRunner.query( + `ALTER TABLE ${TABLE_NAME} ADD COLUMN ${COLUMN_NAME} tinyint(4) NOT NULL DEFAULT '0', ALGORITHM=INPLACE, LOCK=NONE `, + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/1646739309660-PrebuildWorskace-rate-limiter-migration.ts b/components/gitpod-db/src/typeorm/migration/1646739309660-PrebuildWorskace-rate-limiter-migration.ts index e2a40bfc3abb74..301f17fbde035d 100644 --- a/components/gitpod-db/src/typeorm/migration/1646739309660-PrebuildWorskace-rate-limiter-migration.ts +++ b/components/gitpod-db/src/typeorm/migration/1646739309660-PrebuildWorskace-rate-limiter-migration.ts @@ -4,21 +4,29 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { indexExists } from "./helper/helper"; export class PrebuildWorkspaceRateLimiterMigration1646739309660 implements MigrationInterface { - public static readonly TABLE_NAME = "d_b_prebuilt_workspace"; public static readonly INDEX_NAME = "ind_prebuiltWorkspace_cloneURL_creationTime_state"; public static readonly FIELDS = ["cloneURL", "creationTime", "state"]; public async up(queryRunner: QueryRunner): Promise { - if(!(await indexExists(queryRunner, PrebuildWorkspaceRateLimiterMigration1646739309660.TABLE_NAME, PrebuildWorkspaceRateLimiterMigration1646739309660.INDEX_NAME))) { - await queryRunner.query(`CREATE INDEX ${PrebuildWorkspaceRateLimiterMigration1646739309660.INDEX_NAME} ON ${PrebuildWorkspaceRateLimiterMigration1646739309660.TABLE_NAME} (${PrebuildWorkspaceRateLimiterMigration1646739309660.FIELDS.join(', ')})`); + if ( + !(await indexExists( + queryRunner, + PrebuildWorkspaceRateLimiterMigration1646739309660.TABLE_NAME, + PrebuildWorkspaceRateLimiterMigration1646739309660.INDEX_NAME, + )) + ) { + await queryRunner.query( + `CREATE INDEX ${PrebuildWorkspaceRateLimiterMigration1646739309660.INDEX_NAME} ON ${ + PrebuildWorkspaceRateLimiterMigration1646739309660.TABLE_NAME + } (${PrebuildWorkspaceRateLimiterMigration1646739309660.FIELDS.join(", ")})`, + ); } } public async down(queryRunner: QueryRunner): Promise {} - } diff --git a/components/gitpod-db/src/typeorm/migration/1646803519382-PrebuildUpdatableSHA.ts b/components/gitpod-db/src/typeorm/migration/1646803519382-PrebuildUpdatableSHA.ts index cfa98cfbd02239..8d2c5b1485e70d 100644 --- a/components/gitpod-db/src/typeorm/migration/1646803519382-PrebuildUpdatableSHA.ts +++ b/components/gitpod-db/src/typeorm/migration/1646803519382-PrebuildUpdatableSHA.ts @@ -4,18 +4,17 @@ * See License-AGPL.txt in the project root for license information. */ -import {MigrationInterface, QueryRunner} from "typeorm"; +import { MigrationInterface, QueryRunner } from "typeorm"; import { columnExists } from "./helper/helper"; export class PrebuildUpdatableSHA1646803519382 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { if (!(await columnExists(queryRunner, "d_b_prebuilt_workspace_updatable", "commitSHA"))) { - await queryRunner.query("ALTER TABLE d_b_prebuilt_workspace_updatable ADD COLUMN commitSHA varchar(255) NOT NULL DEFAULT ''"); + await queryRunner.query( + "ALTER TABLE d_b_prebuilt_workspace_updatable ADD COLUMN commitSHA varchar(255) NOT NULL DEFAULT ''", + ); } } - public async down(queryRunner: QueryRunner): Promise { - } - + public async down(queryRunner: QueryRunner): Promise {} } diff --git a/components/gitpod-db/src/typeorm/migration/helper/helper.ts b/components/gitpod-db/src/typeorm/migration/helper/helper.ts index be9a7ddad1aae9..9a31fcb809fa13 100644 --- a/components/gitpod-db/src/typeorm/migration/helper/helper.ts +++ b/components/gitpod-db/src/typeorm/migration/helper/helper.ts @@ -6,16 +6,21 @@ import { QueryRunner } from "typeorm"; -export async function createIndexIfNotExist(queryRunner: QueryRunner, tableName: string, indexName: string, columns: string[]): Promise { +export async function createIndexIfNotExist( + queryRunner: QueryRunner, + tableName: string, + indexName: string, + columns: string[], +): Promise { if (columns.length === 0) { throw new Error("createIndexIfNotExist: Columns must not be empty!"); } if (!indexExists(queryRunner, tableName, indexName)) { - const columnsStr = columns.map(cn => `\`${cn}\``).join(", "); + const columnsStr = columns.map((cn) => `\`${cn}\``).join(", "); await queryRunner.query(`CREATE INDEX ${indexName} ON ${tableName} (${columnsStr})`); } -}; +} export async function indexExists(queryRunner: QueryRunner, tableName: string, indexName: string): Promise { const database = queryRunner.connection.options.database; @@ -23,10 +28,10 @@ export async function indexExists(queryRunner: QueryRunner, tableName: string, i `SELECT COUNT(1) AS cnt FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema = '${database}' AND table_name = '${tableName}' - AND index_name = '${indexName}'` + AND index_name = '${indexName}'`, ); return Number.parseInt(countResult[0].cnt) > 0; // for composite indexes this seems to return the number of columns involved -}; +} export async function columnExists(queryRunner: QueryRunner, tableName: string, columnName: string): Promise { const database = queryRunner.connection.options.database; @@ -34,18 +39,17 @@ export async function columnExists(queryRunner: QueryRunner, tableName: string, `SELECT COUNT(1) AS cnt FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = '${database}' AND table_name = '${tableName}' - AND column_name = '${columnName}'` + AND column_name = '${columnName}'`, ); return Number.parseInt(countResult[0].cnt) === 1; -}; +} export async function tableExists(queryRunner: QueryRunner, tableName: string): Promise { const database = queryRunner.connection.options.database; const countResult = await queryRunner.query( `SELECT COUNT(1) AS cnt FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = '${database}' - AND table_name = '${tableName}'` + AND table_name = '${tableName}'`, ); return Number.parseInt(countResult[0].cnt) === 1; -}; - +} diff --git a/components/gitpod-db/src/typeorm/naming-strategy.ts b/components/gitpod-db/src/typeorm/naming-strategy.ts index ee10434bf28225..5338bd33edc89d 100644 --- a/components/gitpod-db/src/typeorm/naming-strategy.ts +++ b/components/gitpod-db/src/typeorm/naming-strategy.ts @@ -4,7 +4,6 @@ * See License-AGPL.txt in the project root for license information. */ - // The following is copied (license: MIT) from https://github.com/typeorm/typeorm/blob/master/src/naming-strategy/DefaultNamingStrategy.ts // to be able to adjust it to be backwards compatible with https://github.com/typeorm/typeorm/blob/0.1.19/src/naming-strategy/DefaultNamingStrategy.ts // by replacing calls to "snakeCase" with "snakeCase_0_1_20" (cmp. https://github.com/typeorm/typeorm/blob/0.1.19/src/util/StringUtils.ts#L13-L20). @@ -14,11 +13,9 @@ import { NamingStrategyInterface } from "typeorm/naming-strategy/NamingStrategyI import { RandomGenerator } from "typeorm/util/RandomGenerator"; import { camelCase, titleCase } from "typeorm/util/StringUtils"; - const snakeCase_0_1_20 = (str: string) => { - return str.replace(/(?:^|\.?)([A-Z])/g, (x, y) => "_" + y.toLowerCase()) - .replace(/^_/, ""); -} + return str.replace(/(?:^|\.?)([A-Z])/g, (x, y) => "_" + y.toLowerCase()).replace(/^_/, ""); +}; /** * Naming strategy that is used by default. @@ -53,8 +50,7 @@ export class DefaultNamingStrategy implements NamingStrategyInterface { columnName(propertyName: string, customName: string, embeddedPrefixes: string[]): string { const name = customName || propertyName; - if (embeddedPrefixes.length) - return camelCase(embeddedPrefixes.join("_")) + titleCase(name); + if (embeddedPrefixes.length) return camelCase(embeddedPrefixes.join("_")) + titleCase(name); return name; } @@ -90,8 +86,7 @@ export class DefaultNamingStrategy implements NamingStrategyInterface { const tableName = this.getTableName(tableOrName); const replacedTableName = tableName.replace(".", "_"); let key = `${replacedTableName}_${clonedColumnNames.join("_")}`; - if (where) - key += `_${where}`; + if (where) key += `_${where}`; return "REL_" + RandomGenerator.sha1(key).substr(0, 26); } @@ -103,7 +98,12 @@ export class DefaultNamingStrategy implements NamingStrategyInterface { return "DF_" + RandomGenerator.sha1(key).substr(0, 27); } - foreignKeyName(tableOrName: Table | string, columnNames: string[], _referencedTablePath?: string, _referencedColumnNames?: string[]): string { + foreignKeyName( + tableOrName: Table | string, + columnNames: string[], + _referencedTablePath?: string, + _referencedColumnNames?: string[], + ): string { // sort incoming column names to avoid issue when ["id", "name"] and ["name", "id"] arrays const clonedColumnNames = [...columnNames]; clonedColumnNames.sort(); @@ -120,8 +120,7 @@ export class DefaultNamingStrategy implements NamingStrategyInterface { const tableName = this.getTableName(tableOrName); const replacedTableName = tableName.replace(".", "_"); let key = `${replacedTableName}_${clonedColumnNames.join("_")}`; - if (where) - key += `_${where}`; + if (where) key += `_${where}`; return "IDX_" + RandomGenerator.sha1(key).substr(0, 26); } @@ -145,10 +144,12 @@ export class DefaultNamingStrategy implements NamingStrategyInterface { return camelCase(relationName + "_" + referencedColumnName); } - joinTableName(firstTableName: string, + joinTableName( + firstTableName: string, secondTableName: string, firstPropertyName: string, - secondPropertyName: string): string { + secondPropertyName: string, + ): string { return snakeCase_0_1_20(firstTableName + "_" + firstPropertyName.replace(/\./gi, "_") + "_" + secondTableName); } diff --git a/components/gitpod-db/src/typeorm/one-time-secret-db-impl.ts b/components/gitpod-db/src/typeorm/one-time-secret-db-impl.ts index 050ff4aeef3e51..56aa1e6f7594ed 100644 --- a/components/gitpod-db/src/typeorm/one-time-secret-db-impl.ts +++ b/components/gitpod-db/src/typeorm/one-time-secret-db-impl.ts @@ -6,8 +6,8 @@ import { inject, injectable } from "inversify"; import { EntityManager, Repository } from "typeorm"; -import { v4 as uuidv4 } from 'uuid'; -import { TypeORM } from './typeorm'; +import { v4 as uuidv4 } from "uuid"; +import { TypeORM } from "./typeorm"; import { OneTimeSecretDB } from "../one-time-secret-db"; import { DBOneTimeSecret } from "./entity/db-one-time-secret"; @@ -28,7 +28,7 @@ export class TypeORMOneTimeSecretDBImpl implements OneTimeSecretDB { deleted: false, expirationTime: expirationTime.toISOString(), id: uuidv4(), - value: secret + value: secret, }; const repo = await this.getRepo(); @@ -64,8 +64,8 @@ export class TypeORMOneTimeSecretDBImpl implements OneTimeSecretDB { } public async pruneExpired(): Promise { - await (await this.getEntityManager()).query("UPDATE d_b_one_time_secret SET deleted = 1 WHERE deleted = 0 AND expirationTime < NOW()"); + await ( + await this.getEntityManager() + ).query("UPDATE d_b_one_time_secret SET deleted = 1 WHERE deleted = 0 AND expirationTime < NOW()"); } - - } diff --git a/components/gitpod-db/src/typeorm/ormconfig.ts b/components/gitpod-db/src/typeorm/ormconfig.ts index 9dadbf493725ac..01991ea143f972 100644 --- a/components/gitpod-db/src/typeorm/ormconfig.ts +++ b/components/gitpod-db/src/typeorm/ormconfig.ts @@ -12,6 +12,6 @@ import { Config } from "../config"; module.exports = { ...TypeORM.defaultOptions(__dirname), - ...(new Config()).dbConfig, - database: process.env.DB_NAME || "gitpod" -} + ...new Config().dbConfig, + database: process.env.DB_NAME || "gitpod", +}; diff --git a/components/gitpod-db/src/typeorm/oss-allowlist-db-impl.ts b/components/gitpod-db/src/typeorm/oss-allowlist-db-impl.ts index f78c4beb6a104f..cf38281badda61 100644 --- a/components/gitpod-db/src/typeorm/oss-allowlist-db-impl.ts +++ b/components/gitpod-db/src/typeorm/oss-allowlist-db-impl.ts @@ -6,13 +6,12 @@ import { inject, injectable } from "inversify"; import { OssAllowListDB } from "../oss-allowlist-db"; -import { TypeORM } from './typeorm'; +import { TypeORM } from "./typeorm"; import { Repository } from "typeorm"; import { DBOssAllowList } from "./entity/db-oss-allowlist"; @injectable() export class OssAllowListDBImpl implements OssAllowListDB { - @inject(TypeORM) typeORM: TypeORM; protected async getEntityManager() { @@ -31,11 +30,11 @@ export class OssAllowListDBImpl implements OssAllowListDB { async hasAny(identities: string[]): Promise { const repo = await this.getRepo(); const count = await repo.count({ - where: identities.map(identity => ({ + where: identities.map((identity) => ({ identity, deleted: false, })), }); return count > 0; } -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/pending-github-event-db-impl.ts b/components/gitpod-db/src/typeorm/pending-github-event-db-impl.ts index 9e64248d37667f..c29184a3cac341 100644 --- a/components/gitpod-db/src/typeorm/pending-github-event-db-impl.ts +++ b/components/gitpod-db/src/typeorm/pending-github-event-db-impl.ts @@ -7,15 +7,20 @@ import { inject, injectable } from "inversify"; import { PendingGithubEvent } from "@gitpod/gitpod-protocol"; import { EntityManager, Repository } from "typeorm"; -import { TypeORM } from './typeorm'; -import { PendingGithubEventDB, PendingGithubEventWithUser, TransactionalPendingGithubEventDBFactory } from "../pending-github-event-db"; +import { TypeORM } from "./typeorm"; +import { + PendingGithubEventDB, + PendingGithubEventWithUser, + TransactionalPendingGithubEventDBFactory, +} from "../pending-github-event-db"; import { DBPendingGithubEvent } from "./entity/db-pending-github-event"; import { DBIdentity } from "./entity/db-identity"; @injectable() export class TypeORMPendingGithubEventDBImpl implements PendingGithubEventDB { @inject(TypeORM) protected readonly typeorm: TypeORM; - @inject(TransactionalPendingGithubEventDBFactory) protected readonly transactionalFactory: TransactionalPendingGithubEventDBFactory; + @inject(TransactionalPendingGithubEventDBFactory) + protected readonly transactionalFactory: TransactionalPendingGithubEventDBFactory; protected async getManager(): Promise { return (await this.typeorm.getConnection()).manager; @@ -32,7 +37,7 @@ export class TypeORMPendingGithubEventDBImpl implements PendingGithubEventDB { public async findByGithubUserID(type: string, accountId: number): Promise { const repo = await this.getRepo(); return await repo - .createQueryBuilder('pghe') + .createQueryBuilder("pghe") .where(`pghe.githubUserId = :accountId AND pghe.type LIKE :tpe`, { accountId, tpe: `${type}%` }) .getMany(); } @@ -45,9 +50,9 @@ export class TypeORMPendingGithubEventDBImpl implements PendingGithubEventDB { public async findWithUser(type: string): Promise { const repo = await this.getRepo(); const res = await repo - .createQueryBuilder('pghe') - .innerJoinAndMapOne('pghe.identity', DBIdentity, 'ident', 'pghe.githubUserId = ident.authId') - .innerJoinAndSelect('ident.user', 'user') + .createQueryBuilder("pghe") + .innerJoinAndMapOne("pghe.identity", DBIdentity, "ident", "pghe.githubUserId = ident.authId") + .innerJoinAndSelect("ident.user", "user") .where('ident.authProviderId = "Public-GitHub"') .andWhere(`ident.deleted != true`) .orderBy("pghe.creationDate", "ASC") @@ -58,7 +63,7 @@ export class TypeORMPendingGithubEventDBImpl implements PendingGithubEventDB { async transaction(code: (db: PendingGithubEventDB) => Promise): Promise { const manager = await this.getManager(); - return await manager.transaction(async manager => { + return await manager.transaction(async (manager) => { const transactionalDB = this.transactionalFactory(manager); return await code(transactionalDB); }); @@ -66,9 +71,7 @@ export class TypeORMPendingGithubEventDBImpl implements PendingGithubEventDB { } export class TransactionalPendingGithubEventDBImpl extends TypeORMPendingGithubEventDBImpl { - - constructor( - protected readonly manager: EntityManager) { + constructor(protected readonly manager: EntityManager) { super(); } @@ -79,4 +82,4 @@ export class TransactionalPendingGithubEventDBImpl extends TypeORMPendingGithubE public async transaction(code: (sb: PendingGithubEventDB) => Promise): Promise { return await code(this); } -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/project-db-impl.ts b/components/gitpod-db/src/typeorm/project-db-impl.ts index 3557ae8b66b41e..858683818682f3 100644 --- a/components/gitpod-db/src/typeorm/project-db-impl.ts +++ b/components/gitpod-db/src/typeorm/project-db-impl.ts @@ -7,7 +7,7 @@ import { inject, injectable } from "inversify"; import { TypeORM } from "./typeorm"; import { Repository } from "typeorm"; -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; import { PartialProject, Project, ProjectEnvVar, ProjectEnvVarWithValue } from "@gitpod/gitpod-protocol"; import { EncryptionService } from "@gitpod/gitpod-protocol/lib/encryption/encryption-service"; import { ProjectDB } from "../project-db"; @@ -17,7 +17,7 @@ import { DBProjectInfo } from "./entity/db-project-info"; function toProjectEnvVar(envVarWithValue: ProjectEnvVarWithValue): ProjectEnvVar { const envVar = { ...envVarWithValue }; - delete (envVar as any)['value']; + delete (envVar as any)["value"]; return envVar; } @@ -57,24 +57,33 @@ export class ProjectDBImpl implements ProjectDB { return []; } const repo = await this.getRepo(); - const q = repo.createQueryBuilder("project") + const q = repo + .createQueryBuilder("project") .where("project.markedDeleted = false") - .andWhere(`project.cloneUrl in (${ cloneUrls.map(u => `'${u}'`).join(", ") })`) + .andWhere(`project.cloneUrl in (${cloneUrls.map((u) => `'${u}'`).join(", ")})`); const projects = await q.getMany(); - const teamIds = Array.from(new Set(projects.map(p => p.teamId).filter(id => !!id))); + const teamIds = Array.from(new Set(projects.map((p) => p.teamId).filter((id) => !!id))); - const teamIdsAndOwners = teamIds.length === 0 ? [] : (await (await this.getEntityManager()).query(` + const teamIdsAndOwners = + teamIds.length === 0 + ? [] + : ((await ( + await this.getEntityManager() + ).query(` SELECT member.teamId AS teamId, user.name AS owner FROM d_b_user AS user LEFT JOIN d_b_team_membership AS member ON (user.id = member.userId) - WHERE member.teamId IN (${teamIds.map(id => `'${id}'`).join(", ")}) + WHERE member.teamId IN (${teamIds.map((id) => `'${id}'`).join(", ")}) AND member.deleted = 0 AND member.role = 'owner' - `)) as { teamId: string, owner: string }[]; + `)) as { teamId: string; owner: string }[]); const result: (Project & { teamOwners?: string[] })[] = []; for (const project of projects) { - result.push({...project, teamOwners: teamIdsAndOwners.filter(i => i.teamId === project.teamId).map(i => i.owner)}); + result.push({ + ...project, + teamOwners: teamIdsAndOwners.filter((i) => i.teamId === project.teamId).map((i) => i.owner), + }); } return result; @@ -95,15 +104,16 @@ export class ProjectDBImpl implements ProjectDB { limit: number, orderBy: keyof Project, orderDir: "DESC" | "ASC", - searchTerm?: string - ): Promise<{ total: number, rows: Project[] }> { + searchTerm?: string, + ): Promise<{ total: number; rows: Project[] }> { const projectRepo = await this.getRepo(); - const queryBuilder = projectRepo.createQueryBuilder('project') + const queryBuilder = projectRepo + .createQueryBuilder("project") .where("project.cloneUrl LIKE :searchTerm", { searchTerm: `%${searchTerm}%` }) .skip(offset) .take(limit) - .orderBy(orderBy, orderDir) + .orderBy(orderBy, orderDir); const [rows, total] = await queryBuilder.getManyAndCount(); return { total, rows }; @@ -118,7 +128,7 @@ export class ProjectDBImpl implements ProjectDB { const repo = await this.getRepo(); const count = await repo.count({ id: partialProject.id, markedDeleted: false }); if (count < 1) { - throw new Error('A project with this ID could not be found'); + throw new Error("A project with this ID could not be found"); } await repo.update(partialProject.id, partialProject); } @@ -138,17 +148,27 @@ export class ProjectDBImpl implements ProjectDB { } } - public async setProjectEnvironmentVariable(projectId: string, name: string, value: string, censored: boolean): Promise { + public async setProjectEnvironmentVariable( + projectId: string, + name: string, + value: string, + censored: boolean, + ): Promise { if (!name) { - throw new Error('Variable name cannot be empty'); + throw new Error("Variable name cannot be empty"); } if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) { - throw new Error('Please choose a variable name containing only letters, numbers, or _, and which doesn\'t start with a number'); + throw new Error( + "Please choose a variable name containing only letters, numbers, or _, and which doesn't start with a number", + ); } const envVarRepo = await this.getProjectEnvVarRepo(); const envVarWithValue = await envVarRepo.findOne({ projectId, name, deleted: false }); if (envVarWithValue) { - await envVarRepo.update({ id: envVarWithValue.id, projectId: envVarWithValue.projectId }, { value, censored }); + await envVarRepo.update( + { id: envVarWithValue.id, projectId: envVarWithValue.projectId }, + { value, censored }, + ); return; } await envVarRepo.save({ @@ -183,7 +203,7 @@ export class ProjectDBImpl implements ProjectDB { const envVarRepo = await this.getProjectEnvVarRepo(); const envVarWithValue = await envVarRepo.findOne({ id: variableId, deleted: false }); if (!envVarWithValue) { - throw new Error('A environment variable with this name could not be found for this project'); + throw new Error("A environment variable with this name could not be found for this project"); } envVarWithValue.deleted = true; await envVarRepo.update({ id: envVarWithValue.id, projectId: envVarWithValue.projectId }, envVarWithValue); diff --git a/components/gitpod-db/src/typeorm/team-db-impl.ts b/components/gitpod-db/src/typeorm/team-db-impl.ts index 7532087a140d48..ae89efe0bb7d12 100644 --- a/components/gitpod-db/src/typeorm/team-db-impl.ts +++ b/components/gitpod-db/src/typeorm/team-db-impl.ts @@ -9,14 +9,13 @@ import { Team, TeamMemberInfo, TeamMemberRole, TeamMembershipInvite, User } from import { inject, injectable } from "inversify"; import { TypeORM } from "./typeorm"; import { Repository } from "typeorm"; -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; import { TeamDB } from "../team-db"; import { DBTeam } from "./entity/db-team"; import { DBTeamMembership } from "./entity/db-team-membership"; import { DBUser } from "./entity/db-user"; import { DBTeamMembershipInvite } from "./entity/db-team-membership-invite"; - @injectable() export class TeamDBImpl implements TeamDB { @inject(TypeORM) typeORM: TypeORM; @@ -46,15 +45,15 @@ export class TeamDBImpl implements TeamDB { limit: number, orderBy: keyof Team, orderDir: "DESC" | "ASC", - searchTerm?: string - ): Promise<{ total: number, rows: Team[] }> { - + searchTerm?: string, + ): Promise<{ total: number; rows: Team[] }> { const teamRepo = await this.getTeamRepo(); - const queryBuilder = teamRepo.createQueryBuilder('team') + const queryBuilder = teamRepo + .createQueryBuilder("team") .where("team.name LIKE :searchTerm", { searchTerm: `%${searchTerm}%` }) .skip(offset) .take(limit) - .orderBy(orderBy, orderDir) + .orderBy(orderBy, orderDir); const [rows, total] = await queryBuilder.getManyAndCount(); return { total, rows }; @@ -62,16 +61,16 @@ export class TeamDBImpl implements TeamDB { public async findTeamById(teamId: string): Promise { const teamRepo = await this.getTeamRepo(); - return teamRepo.findOne({ id: teamId, deleted: false, markedDeleted: false}); + return teamRepo.findOne({ id: teamId, deleted: false, markedDeleted: false }); } public async findMembersByTeam(teamId: string): Promise { const membershipRepo = await this.getMembershipRepo(); const userRepo = await this.getUserRepo(); const memberships = await membershipRepo.find({ teamId, deleted: false }); - const users = await userRepo.findByIds(memberships.map(m => m.userId)); - const infos = users.map(u => { - const m = memberships.find(m => m.userId === u.id)!; + const users = await userRepo.findByIds(memberships.map((m) => m.userId)); + const infos = users.map((u) => { + const m = memberships.find((m) => m.userId === u.id)!; return { userId: u.id, fullName: u.fullName || u.name, @@ -81,15 +80,15 @@ export class TeamDBImpl implements TeamDB { memberSince: m.creationTime, }; }); - return infos.sort((a,b) => a.memberSince < b.memberSince ? 1 : (a.memberSince === b.memberSince ? 0 : -1)); + return infos.sort((a, b) => (a.memberSince < b.memberSince ? 1 : a.memberSince === b.memberSince ? 0 : -1)); } public async findTeamsByUser(userId: string): Promise { const teamRepo = await this.getTeamRepo(); const membershipRepo = await this.getMembershipRepo(); const memberships = await membershipRepo.find({ userId, deleted: false }); - const teams = await teamRepo.findByIds(memberships.map(m => m.teamId)); - return teams.filter(t => !t.markedDeleted); + const teams = await teamRepo.findByIds(memberships.map((m) => m.teamId)); + return teams.filter((t) => !t.markedDeleted); } public async findTeamsByUserAsSoleOwner(userId: string): Promise { @@ -99,7 +98,7 @@ export class TeamDBImpl implements TeamDB { const userTeams = await this.findTeamsByUser(userId); for (const team of userTeams) { const memberships = await this.findMembersByTeam(team.id); - const ownerships = memberships.filter(m => m.role === 'owner'); + const ownerships = memberships.filter((m) => m.role === "owner"); if (ownerships.length === 1 && ownerships[0].userId === userId) { soleOwnedTeams.push(team); } @@ -109,38 +108,41 @@ export class TeamDBImpl implements TeamDB { public async createTeam(userId: string, name: string): Promise { if (!name) { - throw new Error('Team name cannot be empty'); + throw new Error("Team name cannot be empty"); } if (!/^[A-Za-z0-9 '_-]+$/.test(name)) { - throw new Error('Please choose a team name containing only letters, numbers, -, _, \', or spaces.'); + throw new Error("Please choose a team name containing only letters, numbers, -, _, ', or spaces."); } - const slug = name.toLocaleLowerCase().replace(/[ ']/g, '-'); + const slug = name.toLocaleLowerCase().replace(/[ ']/g, "-"); if (blocklist.indexOf(slug) !== -1) { - throw new Error('Creating a team with this name is not allowed'); + throw new Error("Creating a team with this name is not allowed"); } const userRepo = await this.getUserRepo(); - const existingUsers = await userRepo.query('SELECT COUNT(id) AS count FROM d_b_user WHERE fullName LIKE ? OR name LIKE ?', [ name, slug ]); + const existingUsers = await userRepo.query( + "SELECT COUNT(id) AS count FROM d_b_user WHERE fullName LIKE ? OR name LIKE ?", + [name, slug], + ); if (Number.parseInt(existingUsers[0].count) > 0) { - throw new Error('A team cannot have the same name as an existing user'); + throw new Error("A team cannot have the same name as an existing user"); } const teamRepo = await this.getTeamRepo(); const existingTeam = await teamRepo.findOne({ slug, deleted: false, markedDeleted: false }); if (!!existingTeam) { - throw new Error('A team with this name already exists'); + throw new Error("A team with this name already exists"); } const team: Team = { id: uuidv4(), name, slug, creationTime: new Date().toISOString(), - } + }; await teamRepo.save(team); const membershipRepo = await this.getMembershipRepo(); await membershipRepo.save({ id: uuidv4(), teamId: team.id, userId, - role: 'owner', + role: "owner", creationTime: team.creationTime, }); return team; @@ -159,7 +161,7 @@ export class TeamDBImpl implements TeamDB { const teamRepo = await this.getTeamRepo(); const team = await teamRepo.findOne(teamId); if (!team || !!team.deleted) { - throw new Error('A team with this ID could not be found'); + throw new Error("A team with this ID could not be found"); } const membershipRepo = await this.getMembershipRepo(); const membership = await membershipRepo.findOne({ teamId, userId, deleted: false }); @@ -170,7 +172,7 @@ export class TeamDBImpl implements TeamDB { id: uuidv4(), teamId: team.id, userId, - role: 'member', + role: "member", creationTime: new Date().toISOString(), }); } @@ -179,12 +181,12 @@ export class TeamDBImpl implements TeamDB { const teamRepo = await this.getTeamRepo(); const team = await teamRepo.findOne(teamId); if (!team || !!team.deleted) { - throw new Error('A team with this ID could not be found'); + throw new Error("A team with this ID could not be found"); } const membershipRepo = await this.getMembershipRepo(); const membership = await membershipRepo.findOne({ teamId, userId, deleted: false }); if (!membership) { - throw new Error('The user is not currently a member of this team'); + throw new Error("The user is not currently a member of this team"); } membership.role = role; await membershipRepo.save(membership); @@ -194,12 +196,12 @@ export class TeamDBImpl implements TeamDB { const teamRepo = await this.getTeamRepo(); const team = await teamRepo.findOne(teamId); if (!team || !!team.deleted) { - throw new Error('A team with this ID could not be found'); + throw new Error("A team with this ID could not be found"); } const membershipRepo = await this.getMembershipRepo(); const membership = await membershipRepo.findOne({ teamId, userId, deleted: false }); if (!membership) { - throw new Error('You are not currently a member of this team'); + throw new Error("You are not currently a member of this team"); } membership.deleted = true; await membershipRepo.save(membership); @@ -209,32 +211,32 @@ export class TeamDBImpl implements TeamDB { const inviteRepo = await this.getMembershipInviteRepo(); const invite = await inviteRepo.findOne(inviteId); if (!invite) { - throw new Error('No invite found for the given ID.'); + throw new Error("No invite found for the given ID."); } return invite; } - public async findGenericInviteByTeamId(teamId: string): Promise { + public async findGenericInviteByTeamId(teamId: string): Promise { const inviteRepo = await this.getMembershipInviteRepo(); const all = await inviteRepo.find({ teamId }); - return all.filter(i => i.invalidationTime === '' && !i.invitedEmail)[0]; + return all.filter((i) => i.invalidationTime === "" && !i.invitedEmail)[0]; } public async resetGenericInvite(teamId: string): Promise { const inviteRepo = await this.getMembershipInviteRepo(); const invite = await this.findGenericInviteByTeamId(teamId); - if (invite && invite.invalidationTime === '') { + if (invite && invite.invalidationTime === "") { invite.invalidationTime = new Date().toISOString(); await inviteRepo.save(invite); } - const newInvite :TeamMembershipInvite = { + const newInvite: TeamMembershipInvite = { id: uuidv4(), creationTime: new Date().toISOString(), - invalidationTime: '', - role: 'member', - teamId - } + invalidationTime: "", + role: "member", + teamId, + }; await inviteRepo.save(newInvite); return newInvite; } diff --git a/components/gitpod-db/src/typeorm/team-subscription-db-impl.ts b/components/gitpod-db/src/typeorm/team-subscription-db-impl.ts index 203a527696a3a4..7f14ca43441b73 100644 --- a/components/gitpod-db/src/typeorm/team-subscription-db-impl.ts +++ b/components/gitpod-db/src/typeorm/team-subscription-db-impl.ts @@ -20,7 +20,7 @@ export class TeamSubscriptionDBImpl implements TeamSubscriptionDB { async transaction(code: (db: TeamSubscriptionDB) => Promise): Promise { const manager = await this.getEntityManager(); - return await manager.transaction(async manager => { + return await manager.transaction(async (manager) => { return await code(new TransactionalTeamSubscriptionDBImpl(manager)); }); } @@ -53,22 +53,27 @@ export class TeamSubscriptionDBImpl implements TeamSubscriptionDB { async findTeamSubscriptionBySlotId(slotId: string): Promise { const repo = await this.getRepo(); - const query = repo.createQueryBuilder("ts") + const query = repo + .createQueryBuilder("ts") .leftJoinAndMapOne("ts.id", DBTeamSubscriptionSlot, "slot", "slot.teamSubscriptionId = ts.id") .where("slot.id = :slotId", { slotId }); return query.getOne(); } - async findTeamSubscriptionByPaymentRef(userId: string, paymentReference: string): Promise { + async findTeamSubscriptionByPaymentRef( + userId: string, + paymentReference: string, + ): Promise { const repo = await this.getRepo(); return repo.findOne({ userId, paymentReference }); } async findTeamSubscriptionsForUser(userId: string, date: string): Promise { const repo = await this.getRepo(); - const query = repo.createQueryBuilder('ts') - .where('ts.userId = :userId', { userId: userId }) - .andWhere('ts.startDate <= :date', { date: date }) + const query = repo + .createQueryBuilder("ts") + .where("ts.userId = :userId", { userId: userId }) + .andWhere("ts.startDate <= :date", { date: date }) .andWhere('ts.endDate = "" OR ts.endDate > :date', { date: date }); return query.getMany(); } @@ -87,7 +92,7 @@ export class TeamSubscriptionDBImpl implements TeamSubscriptionDB { const v = (dbSlot as any)[k]; if (k in dbSlot && v === undefined) { // typeorm ignores undefined as 'no data set' but we want to override old values! - (dbSlot as any)[k] = ''; + (dbSlot as any)[k] = ""; } } return (await this.getSlotsRepo()).save(dbSlot); @@ -117,4 +122,4 @@ export class TransactionalTeamSubscriptionDBImpl extends TeamSubscriptionDBImpl async getEntityManager(): Promise { return this.manager; } -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/terms-acceptance-db-impl.ts b/components/gitpod-db/src/typeorm/terms-acceptance-db-impl.ts index 4d0de55829fb58..056b30b8aa9cc0 100644 --- a/components/gitpod-db/src/typeorm/terms-acceptance-db-impl.ts +++ b/components/gitpod-db/src/typeorm/terms-acceptance-db-impl.ts @@ -13,7 +13,6 @@ import { DBTermsAcceptanceEntry } from "./entity/db-terms-acceptance-entry"; @injectable() export class TermsAcceptanceDBImpl implements TermsAcceptanceDB { - @inject(TypeORM) typeORM: TypeORM; protected async getEntityManager(): Promise { @@ -26,7 +25,8 @@ export class TermsAcceptanceDBImpl implements TermsAcceptanceDB { async getAcceptedRevision(userId: string): Promise { const repo = await this.getTermsAcceptanceRepo(); - const query = repo.createQueryBuilder(`terms_acceptance`) + const query = repo + .createQueryBuilder(`terms_acceptance`) .where(`terms_acceptance.userId = :userId`, { userId }); const result = await query.getMany(); return result[0]; @@ -36,5 +36,4 @@ export class TermsAcceptanceDBImpl implements TermsAcceptanceDB { await repo.save({ userId, termsRevision, acceptionTime: new Date().toISOString() }); // if entity does not exist in the database then inserts, otherwise updates. } - } diff --git a/components/gitpod-db/src/typeorm/theia-plugin-db-impl.ts b/components/gitpod-db/src/typeorm/theia-plugin-db-impl.ts index 3339836502e988..05f3078c2372cd 100644 --- a/components/gitpod-db/src/typeorm/theia-plugin-db-impl.ts +++ b/components/gitpod-db/src/typeorm/theia-plugin-db-impl.ts @@ -10,11 +10,10 @@ import { TypeORM } from "./typeorm"; import { TheiaPluginDB } from "../theia-plugin-db"; import { DBTheiaPlugin } from "./entity/db-theia-plugin"; import { TheiaPlugin } from "@gitpod/gitpod-protocol"; -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; @injectable() export class TheiaPluginDBImpl implements TheiaPluginDB { - @inject(TypeORM) typeORM: TypeORM; protected async getEntityManager(): Promise { @@ -25,7 +24,12 @@ export class TheiaPluginDBImpl implements TheiaPluginDB { return (await this.getEntityManager()).getRepository(DBTheiaPlugin); } - async newPlugin(userId: string, pluginName: string, bucketName: string, pathFn: (id: string) => string): Promise { + async newPlugin( + userId: string, + pluginName: string, + bucketName: string, + pathFn: (id: string) => string, + ): Promise { const id = uuidv4(); const newPlugin: TheiaPlugin = { id, @@ -33,7 +37,7 @@ export class TheiaPluginDBImpl implements TheiaPluginDB { userId, bucketName, path: pathFn(id), - state: TheiaPlugin.State.Uploading + state: TheiaPlugin.State.Uploading, }; return await this.storePlugin(newPlugin); } @@ -55,23 +59,22 @@ export class TheiaPluginDBImpl implements TheiaPluginDB { async findByPluginId(pluginId: string): Promise { const repo = await this.getTheiaPluginRepo(); - const query = repo.createQueryBuilder('theia_plugin') - .where(`theia_plugin.pluginId = :pluginId`, { pluginId }); + const query = repo.createQueryBuilder("theia_plugin").where(`theia_plugin.pluginId = :pluginId`, { pluginId }); return query.getMany(); } - async exists(pluginId: string, predicate: { state?: TheiaPlugin.State, hash?: string }): Promise { + async exists(pluginId: string, predicate: { state?: TheiaPlugin.State; hash?: string }): Promise { const repo = await this.getTheiaPluginRepo(); - const query = repo.createQueryBuilder('theia_plugin') - .select('1') + const query = repo + .createQueryBuilder("theia_plugin") + .select("1") .where(`theia_plugin.pluginId = :pluginId`, { pluginId }); if (predicate.state) { - query.andWhere(`theia_plugin.state = :state`, { state: predicate.state }) + query.andWhere(`theia_plugin.state = :state`, { state: predicate.state }); } if (predicate.hash) { - query.andWhere(`theia_plugin.hash = :hash`, { hash: predicate.hash }) + query.andWhere(`theia_plugin.hash = :hash`, { hash: predicate.hash }); } return (await query.getCount()) > 0; } - } diff --git a/components/gitpod-db/src/typeorm/transformer.ts b/components/gitpod-db/src/typeorm/transformer.ts index 68a74d3cb98b63..552f62bddcd161 100644 --- a/components/gitpod-db/src/typeorm/transformer.ts +++ b/components/gitpod-db/src/typeorm/transformer.ts @@ -7,21 +7,20 @@ import { ValueTransformer } from "typeorm/decorator/options/ValueTransformer"; import { EncryptionService } from "@gitpod/gitpod-protocol/lib/encryption/encryption-service"; - export namespace Transformer { export const MAP_EMPTY_STR_TO_UNDEFINED: ValueTransformer = { to(value: any): any { if (value === undefined) { - return ''; + return ""; } return value; }, from(value: any): any { - if (value === '') { + if (value === "") { return undefined; } return value; - } + }, }; export const MAP_ISO_STRING_TO_TIMESTAMP_DROP: ValueTransformer = { @@ -36,19 +35,19 @@ export namespace Transformer { from(value: any): any { // From TIMESTAMP to ISO string return new Date(Date.parse(value)).toISOString(); - } + }, }; export const SIMPLE_JSON = (defaultValue: any) => { - return { + return { to(value: any): any { return JSON.stringify(value || defaultValue); }, from(value: any): any { return JSON.parse(value); - } + }, }; - } + }; export const encrypted = (encryptionServiceProvider: () => EncryptionService): ValueTransformer => { return { @@ -57,18 +56,17 @@ export namespace Transformer { }, from(value: any): any { return encryptionServiceProvider().decrypt(value); - } + }, }; - } + }; export const compose = (upper: ValueTransformer, lower: ValueTransformer) => { return new CompositeValueTransformer(upper, lower); - } + }; } export class CompositeValueTransformer implements ValueTransformer { - constructor(protected readonly upper: ValueTransformer, - protected readonly lower: ValueTransformer) {} + constructor(protected readonly upper: ValueTransformer, protected readonly lower: ValueTransformer) {} to(value: any): any { return this.lower.to(this.upper.to(value)); diff --git a/components/gitpod-db/src/typeorm/typeorm.ts b/components/gitpod-db/src/typeorm/typeorm.ts index da274437e5b4d9..16c1fb4e52cbc1 100644 --- a/components/gitpod-db/src/typeorm/typeorm.ts +++ b/components/gitpod-db/src/typeorm/typeorm.ts @@ -10,18 +10,18 @@ import { Connection, createConnection, ConnectionOptions, getConnectionManager, import { Config } from "../config"; import { DefaultNamingStrategy } from "./naming-strategy"; -export const TypeORMOptions = Symbol('TypeORMOptions'); +export const TypeORMOptions = Symbol("TypeORMOptions"); @injectable() export class TypeORM { - static readonly DEFAULT_CONNECTION_NAME = 'default'; + static readonly DEFAULT_CONNECTION_NAME = "default"; static readonly UUID_COLUMN_TYPE: PrimaryColumnOptions = { - type: 'char', - length: 36 + type: "char", + length: 36, }; static readonly WORKSPACE_ID_COLUMN_TYPE: PrimaryColumnOptions = { - type: 'char', - length: 36 + type: "char", + length: 36, }; static defaultOptions(dir: string): ConnectionOptions { @@ -32,23 +32,15 @@ export class TypeORM { migrationsRun: false, logging: false, connectTimeout: 20000, - timezone: 'utc', - charset: 'utf8mb4', - entities: [ - dir + "/entity/**/*.js", - dir + "/entity/**/*.ts" - ], - migrations: [ - dir + "/migration/*.js", - dir + "/migration/*.ts" - ], - subscribers: [ - dir + "/subscriber/**/*.js" - ], + timezone: "utc", + charset: "utf8mb4", + entities: [dir + "/entity/**/*.js", dir + "/entity/**/*.ts"], + migrations: [dir + "/migration/*.js", dir + "/migration/*.ts"], + subscribers: [dir + "/subscriber/**/*.js"], cli: { entitiesDir: "src/typeorm/entity", - migrationsDir: "src/typeorm/migration", - subscribersDir: "src/typeorm/subscriber" + migrationsDir: "src/typeorm/migration", + subscribersDir: "src/typeorm/subscriber", }, namingStrategy: new DefaultNamingStrategy(), }; @@ -57,13 +49,15 @@ export class TypeORM { protected _connection?: Connection = undefined; protected readonly _options: ConnectionOptions; - constructor(@inject(Config) protected readonly config: Config, - @inject(TypeORMOptions) @optional() protected readonly options?: Partial) { + constructor( + @inject(Config) protected readonly config: Config, + @inject(TypeORMOptions) @optional() protected readonly options?: Partial, + ) { options = options || {}; this._options = { ...TypeORM.defaultOptions(__dirname), ...this.config.dbConfig, - ...options + ...options, } as ConnectionOptions; } @@ -75,7 +69,7 @@ export class TypeORM { } else { this._connection = await createConnection({ ...this._options, - name: TypeORM.DEFAULT_CONNECTION_NAME + name: TypeORM.DEFAULT_CONNECTION_NAME, }); } } @@ -85,4 +79,4 @@ export class TypeORM { public async connect() { await this.getConnection(); } -} \ No newline at end of file +} diff --git a/components/gitpod-db/src/typeorm/user-db-impl.ts b/components/gitpod-db/src/typeorm/user-db-impl.ts index 68396efb280603..e96a0a544a354e 100644 --- a/components/gitpod-db/src/typeorm/user-db-impl.ts +++ b/components/gitpod-db/src/typeorm/user-db-impl.ts @@ -4,22 +4,39 @@ * See License-AGPL.txt in the project root for license information. */ -import * as crypto from 'crypto'; -import { GitpodToken, GitpodTokenType, Identity, IdentityLookup, Token, TokenEntry, User, UserEnvVar } from "@gitpod/gitpod-protocol"; +import * as crypto from "crypto"; +import { + GitpodToken, + GitpodTokenType, + Identity, + IdentityLookup, + Token, + TokenEntry, + User, + UserEnvVar, +} from "@gitpod/gitpod-protocol"; import { EncryptionService } from "@gitpod/gitpod-protocol/lib/encryption/encryption-service"; -import { DateInterval, ExtraAccessTokenFields, GrantIdentifier, OAuthClient, OAuthScope, OAuthToken, OAuthUser } from "@jmondi/oauth2-server"; +import { + DateInterval, + ExtraAccessTokenFields, + GrantIdentifier, + OAuthClient, + OAuthScope, + OAuthToken, + OAuthUser, +} from "@jmondi/oauth2-server"; import { inject, injectable, postConstruct } from "inversify"; import { EntityManager, Repository } from "typeorm"; -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; import { BUILTIN_WORKSPACE_PROBE_USER_ID, MaybeUser, PartialUserUpdate, UserDB } from "../user-db"; import { DBGitpodToken } from "./entity/db-gitpod-token"; import { DBIdentity } from "./entity/db-identity"; import { DBTokenEntry } from "./entity/db-token-entry"; -import { DBUser } from './entity/db-user'; +import { DBUser } from "./entity/db-user"; import { DBUserEnvVar } from "./entity/db-user-env-vars"; import { DBWorkspace } from "./entity/db-workspace"; -import { TypeORM } from './typeorm'; -import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; +import { TypeORM } from "./typeorm"; +import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; // OAuth token expiry const tokenExpiryInFuture = new DateInterval("7d"); @@ -29,7 +46,6 @@ export let encryptionService: EncryptionService; @injectable() export class TypeORMUserDBImpl implements UserDB { - @inject(TypeORM) protected readonly typeorm: TypeORM; @inject(EncryptionService) protected readonly encryptionService: EncryptionService; @@ -52,7 +68,7 @@ export class TypeORMUserDBImpl implements UserDB { async transaction(code: (db: UserDB) => Promise): Promise { const manager = await this.getEntityManager(); - return await manager.transaction(async manager => { + return await manager.transaction(async (manager) => { return await code(new TransactionalUserDBImpl(manager)); }); } @@ -79,8 +95,12 @@ export class TypeORMUserDBImpl implements UserDB { creationDate: new Date().toISOString(), identities: [], additionalData: { - ideSettings: { defaultIde: 'code' }, - emailNotificationSettings: { allowsChangelogMail: true, allowsDevXMail: true, allowsOnboardingMail: true } + ideSettings: { defaultIde: "code" }, + emailNotificationSettings: { + allowsChangelogMail: true, + allowsDevXMail: true, + allowsOnboardingMail: true, + }, }, }; await this.storeUser(user); @@ -109,10 +129,12 @@ export class TypeORMUserDBImpl implements UserDB { public async findUserByIdentity(identity: IdentityLookup): Promise { const userRepo = await this.getUserRepo(); - const qBuilder = userRepo.createQueryBuilder('user') + const qBuilder = userRepo + .createQueryBuilder("user") .leftJoinAndSelect("user.identities", "identity") - .where(qb => { - const subQuery = qb.subQuery() + .where((qb) => { + const subQuery = qb + .subQuery() .select("user1.id") .from(DBUser, "user1") .leftJoin("user1.identities", "identity") @@ -121,34 +143,39 @@ export class TypeORMUserDBImpl implements UserDB { .andWhere(`identity.deleted != true`) .andWhere(`user1.markedDeleted != true`) .getQuery(); - return `user.id IN ${subQuery}` - }) + return `user.id IN ${subQuery}`; + }); return qBuilder.getOne(); } public async findUsersByEmail(email: string): Promise { const userRepo = await this.getUserRepo(); - const qBuilder = userRepo.createQueryBuilder('user') + const qBuilder = userRepo + .createQueryBuilder("user") .leftJoinAndSelect("user.identities", "identity") - .where(qb => { - const subQuery = qb.subQuery() + .where((qb) => { + const subQuery = qb + .subQuery() .select("identity1.userId") .from(DBIdentity, "identity1") .where(`identity1.primaryEmail = :email`, { email: email }) .andWhere(`identity1.deleted != true`) .getQuery(); - return `user.markedDeleted != true AND user.id IN ${subQuery}` + return `user.markedDeleted != true AND user.id IN ${subQuery}`; }); const result = await qBuilder.getMany(); let order = (u1: User, u2: User) => u1.creationDate.localeCompare(u2.creationDate); if (result.length > 1) { - const lastActivities = (await (await this.getEntityManager()).query(` + const lastActivities = (await ( + await this.getEntityManager() + ).query(` SELECT ws.ownerId AS userId, MAX(wsi.creationTime) AS lastActivity FROM d_b_workspace_instance AS wsi LEFT JOIN d_b_workspace AS ws ON (wsi.workspaceId = ws.id) - WHERE ws.ownerId IN (${result.map(u => `'${u.id}'`).join(", ")}) + WHERE ws.ownerId IN (${result.map((u) => `'${u.id}'`).join(", ")}) GROUP BY ws.ownerId - `)) as { userId?: string, lastActivity?: string }[]; - const lastActivity = (u: User) => lastActivities.filter(({ userId }) => userId === u.id).map(({ lastActivity }) => lastActivity)[0]; + `)) as { userId?: string; lastActivity?: string }[]; + const lastActivity = (u: User) => + lastActivities.filter(({ userId }) => userId === u.id).map(({ lastActivity }) => lastActivity)[0]; order = (u1: User, u2: User) => { const a1 = lastActivity(u1) || u1.creationDate; const a2 = lastActivity(u2) || u2.creationDate; @@ -156,17 +183,22 @@ export class TypeORMUserDBImpl implements UserDB { if (!a1) return 1; if (!a2) return -1; return -1 * a1.localeCompare(a2); - } + }; } return result.sort(order); } - public async findUserByGitpodToken(tokenHash: string, tokenType?: GitpodTokenType): Promise<{ user: User, token: GitpodToken } | undefined> { + public async findUserByGitpodToken( + tokenHash: string, + tokenType?: GitpodTokenType, + ): Promise<{ user: User; token: GitpodToken } | undefined> { const repo = await this.getGitpodTokenRepo(); - const qBuilder = repo.createQueryBuilder('gitpodToken') - .leftJoinAndSelect("gitpodToken.user", "user"); + const qBuilder = repo.createQueryBuilder("gitpodToken").leftJoinAndSelect("gitpodToken.user", "user"); if (!!tokenType) { - qBuilder.where("gitpodToken.tokenHash = :tokenHash AND gitpodToken.type = :tokenType", { tokenHash, tokenType }); + qBuilder.where("gitpodToken.tokenHash = :tokenHash AND gitpodToken.type = :tokenType", { + tokenHash, + tokenType, + }); } else { qBuilder.where("gitpodToken.tokenHash = :tokenHash", { tokenHash }); } @@ -180,18 +212,16 @@ export class TypeORMUserDBImpl implements UserDB { } public async findGitpodTokensOfUser(userId: string, tokenHash: string): Promise { - const repo = await this.getGitpodTokenRepo() - const qBuilder = repo.createQueryBuilder('gitpodToken') - .leftJoinAndSelect("gitpodToken.user", "user"); - qBuilder.where('user.id = :userId AND gitpodToken.tokenHash = :tokenHash', { userId, tokenHash }); + const repo = await this.getGitpodTokenRepo(); + const qBuilder = repo.createQueryBuilder("gitpodToken").leftJoinAndSelect("gitpodToken.user", "user"); + qBuilder.where("user.id = :userId AND gitpodToken.tokenHash = :tokenHash", { userId, tokenHash }); return qBuilder.getOne(); } public async findAllGitpodTokensOfUser(userId: string): Promise { - const repo = await this.getGitpodTokenRepo() - const qBuilder = repo.createQueryBuilder('gitpodToken') - .leftJoinAndSelect("gitpodToken.user", "user"); - qBuilder.where('user.id = :userId', { userId }); + const repo = await this.getGitpodTokenRepo(); + const qBuilder = repo.createQueryBuilder("gitpodToken").leftJoinAndSelect("gitpodToken.user", "user"); + qBuilder.where("user.id = :userId", { userId }); return qBuilder.getMany(); } @@ -202,26 +232,33 @@ export class TypeORMUserDBImpl implements UserDB { public async deleteGitpodToken(tokenHash: string): Promise { const repo = await this.getGitpodTokenRepo(); - await repo.query(` + await repo.query( + ` UPDATE d_b_gitpod_token AS gt SET gt.deleted = TRUE WHERE tokenHash = ?; - `, [tokenHash]); + `, + [tokenHash], + ); } public async deleteGitpodTokensNamedLike(userId: string, namePattern: string): Promise { const repo = await this.getGitpodTokenRepo(); - await repo.query(` + await repo.query( + ` UPDATE d_b_gitpod_token AS gt SET gt.deleted = TRUE WHERE userId = ? AND name LIKE ? - `, [userId, namePattern]); + `, + [userId, namePattern], + ); } public async findIdentitiesByName(identity: Identity): Promise { const repo = await this.getIdentitiesRepo(); - const qBuilder = repo.createQueryBuilder('identity') + const qBuilder = repo + .createQueryBuilder("identity") .where(`identity.authProviderId = :authProviderId`, { authProviderId: identity.authProviderId }) .andWhere(`identity.deleted != true`) .andWhere(`identity.authName = :authName`, { authName: identity.authName }); @@ -258,13 +295,16 @@ export class TypeORMUserDBImpl implements UserDB { public async deleteExpiredTokenEntries(date: string): Promise { const repo = await this.getTokenRepo(); - await repo.query(` + await repo.query( + ` UPDATE d_b_token_entry AS te SET te.deleted = TRUE WHERE te.expiryDate != '' AND te.refreshable != 1 AND te.expiryDate <= ?; - `, [date]); + `, + [date], + ); } public async updateTokenEntry(tokenEntry: Partial & Pick): Promise { @@ -291,21 +331,22 @@ export class TypeORMUserDBImpl implements UserDB { if (tokenEntries.length === 0) { return undefined; } - return tokenEntries.sort((a, b) => `${a.token.updateDate}`.localeCompare(`${b.token.updateDate}`)).reverse()[0]?.token; + return tokenEntries.sort((a, b) => `${a.token.updateDate}`.localeCompare(`${b.token.updateDate}`)).reverse()[0] + ?.token; } public async findTokensForIdentity(identity: Identity, includeDeleted?: boolean): Promise { const repo = await this.getTokenRepo(); const entry = await repo.find({ authProviderId: identity.authProviderId, authId: identity.authId }); - return entry.filter(te => includeDeleted || !te.deleted); + return entry.filter((te) => includeDeleted || !te.deleted); } protected mapUserToDBUser(user: User): DBUser { const dbUser = user as DBUser; // Here we need to fill the pseudo column 'user' in DBIdentity (see there for details) - dbUser.identities.forEach(id => id.user = dbUser); + dbUser.identities.forEach((id) => (id.user = dbUser)); // TODO deprecated: Remove once we delete that column - dbUser.identities.forEach(id => id.tokens = []); + dbUser.identities.forEach((id) => (id.tokens = [])); return dbUser; } @@ -315,7 +356,7 @@ export class TypeORMUserDBImpl implements UserDB { WHERE markedDeleted != true`; if (excludeBuiltinUsers) { query = `${query} - AND id <> '${BUILTIN_WORKSPACE_PROBE_USER_ID}'` + AND id <> '${BUILTIN_WORKSPACE_PROBE_USER_ID}'`; } const res = await userRepo.query(query); const count = res[0].cnt; @@ -329,8 +370,8 @@ export class TypeORMUserDBImpl implements UserDB { public async getEnvVars(userId: string): Promise { const dbrepo = await this.getUserEnvVarRepo(); - const allVars = (await dbrepo.find({ where: { userId } })); - return allVars.filter(envVar => !envVar.deleted); + const allVars = await dbrepo.find({ where: { userId } }); + return allVars.filter((envVar) => !envVar.deleted); } public async deleteEnvVar(envVar: UserEnvVar): Promise { @@ -339,27 +380,42 @@ export class TypeORMUserDBImpl implements UserDB { await repo.save(envVar); } - public async findAllUsers(offset: number, limit: number, orderBy: keyof User, orderDir: "DESC" | "ASC", searchTerm?: string, minCreationDate?: Date, maxCreationDate?: Date, excludeBuiltinUsers?: boolean): Promise<{ total: number, rows: User[] }> { + public async findAllUsers( + offset: number, + limit: number, + orderBy: keyof User, + orderDir: "DESC" | "ASC", + searchTerm?: string, + minCreationDate?: Date, + maxCreationDate?: Date, + excludeBuiltinUsers?: boolean, + ): Promise<{ total: number; rows: User[] }> { const userRepo = await this.getUserRepo(); - const qBuilder = userRepo.createQueryBuilder('user') - .leftJoinAndSelect("user.identities", "identity"); + const qBuilder = userRepo.createQueryBuilder("user").leftJoinAndSelect("user.identities", "identity"); if (searchTerm) { - qBuilder.andWhere(`user.name LIKE :searchTerm + qBuilder.andWhere( + `user.name LIKE :searchTerm OR user.fullName LIKE :searchTerm OR user.id in ( SELECT userid from d_b_identity AS i WHERE i.deleted != true AND i.primaryEmail LIKE :searchTerm - )`, { searchTerm: '%' + searchTerm + '%' }); + )`, + { searchTerm: "%" + searchTerm + "%" }, + ); } if (minCreationDate) { - qBuilder.andWhere("user.creationDate >= :minCreationDate", { minCreationDate: minCreationDate.toISOString() }); + qBuilder.andWhere("user.creationDate >= :minCreationDate", { + minCreationDate: minCreationDate.toISOString(), + }); } if (maxCreationDate) { - qBuilder.andWhere("user.creationDate < :maxCreationDate", { maxCreationDate: maxCreationDate.toISOString() }); + qBuilder.andWhere("user.creationDate < :maxCreationDate", { + maxCreationDate: maxCreationDate.toISOString(), + }); } if (excludeBuiltinUsers) { - qBuilder.andWhere("user.id <> :userId", { userId: BUILTIN_WORKSPACE_PROBE_USER_ID }) + qBuilder.andWhere("user.id <> :userId", { userId: BUILTIN_WORKSPACE_PROBE_USER_ID }); } qBuilder.orderBy("user." + orderBy, orderDir); qBuilder.skip(offset).take(limit).select(); @@ -373,7 +429,12 @@ export class TypeORMUserDBImpl implements UserDB { // OAuthAuthCodeRepository // OAuthUserRepository - public async getUserByCredentials(identifier: string, password?: string, grantType?: GrantIdentifier, client?: OAuthClient): Promise { + public async getUserByCredentials( + identifier: string, + password?: string, + grantType?: GrantIdentifier, + client?: OAuthClient, + ): Promise { const user = await this.findUserById(identifier); if (user) { return { @@ -392,7 +453,7 @@ export class TypeORMUserDBImpl implements UserDB { async issueToken(client: OAuthClient, scopes: OAuthScope[], user?: OAuthUser): Promise { const expiry = tokenExpiryInFuture.getEndDate(); return { - accessToken: crypto.randomBytes(30).toString('hex'), + accessToken: crypto.randomBytes(30).toString("hex"), accessTokenExpiresAt: expiry, client, user, @@ -411,7 +472,7 @@ export class TypeORMUserDBImpl implements UserDB { // Does the token already exist? var dbToken: GitpodToken & { user: DBUser }; - const tokenHash = crypto.createHash('sha256').update(accessToken.accessToken, 'utf8').digest("hex"); + const tokenHash = crypto.createHash("sha256").update(accessToken.accessToken, "utf8").digest("hex"); const userAndToken = await this.findUserByGitpodToken(tokenHash); if (userAndToken) { // Yes, update it (~) @@ -425,7 +486,7 @@ export class TypeORMUserDBImpl implements UserDB { } else { var user: MaybeUser; if (accessToken.user) { - user = await this.findUserById(accessToken.user.id) + user = await this.findUserById(accessToken.user.id); } dbToken = { tokenHash, @@ -439,8 +500,8 @@ export class TypeORMUserDBImpl implements UserDB { } } async revoke(accessTokenToken: OAuthToken): Promise { - const tokenHash = crypto.createHash('sha256').update(accessTokenToken.accessToken, 'utf8').digest("hex"); - this.deleteGitpodToken(tokenHash) + const tokenHash = crypto.createHash("sha256").update(accessTokenToken.accessToken, "utf8").digest("hex"); + this.deleteGitpodToken(tokenHash); } async isRefreshTokenRevoked(refreshToken: OAuthToken): Promise { return Date.now() > (refreshToken.refreshTokenExpiresAt?.getTime() ?? 0); @@ -451,7 +512,6 @@ export class TypeORMUserDBImpl implements UserDB { } export class TransactionalUserDBImpl extends TypeORMUserDBImpl { - constructor(protected readonly manager: EntityManager) { super(); } diff --git a/components/gitpod-db/src/typeorm/user-message-views-db-impl.ts b/components/gitpod-db/src/typeorm/user-message-views-db-impl.ts index 6f72ab3dc0c001..ee6b196eb784e9 100644 --- a/components/gitpod-db/src/typeorm/user-message-views-db-impl.ts +++ b/components/gitpod-db/src/typeorm/user-message-views-db-impl.ts @@ -12,7 +12,6 @@ import { DBUserMessageViewEntry } from "./entity/db-user-message-view-entry"; @injectable() export class TypeORMUserMessageViewsDBImpl implements UserMessageViewsDB { - @inject(TypeORM) typeORM: TypeORM; protected async getEntityManager(): Promise { @@ -25,21 +24,24 @@ export class TypeORMUserMessageViewsDBImpl implements UserMessageViewsDB { async didViewMessage(userId: string, userMessageId: string): Promise { const repo = await this.getUserMessageViewsRepo(); - let query = repo.createQueryBuilder('view') - .where('view.userMessageId = :userMessageId AND view.userId = :userId', { userId: userId, userMessageId: userMessageId }); + let query = repo + .createQueryBuilder("view") + .where("view.userMessageId = :userMessageId AND view.userId = :userId", { + userId: userId, + userMessageId: userMessageId, + }); const count = await query.getCount(); return count > 0; } async markAsViewed(userId: string, messageIds: string[]): Promise { const repo = await this.getUserMessageViewsRepo(); - const newEntries = messageIds.map(id => { + const newEntries = messageIds.map((id) => { const newEntry = new DBUserMessageViewEntry(); newEntry.userId = userId; newEntry.userMessageId = id; return newEntry; - }) - await Promise.all(newEntries.map(entry => repo.save(entry))); + }); + await Promise.all(newEntries.map((entry) => repo.save(entry))); } - } diff --git a/components/gitpod-db/src/typeorm/user-storage-resources-db-impl.ts b/components/gitpod-db/src/typeorm/user-storage-resources-db-impl.ts index 2d61f31b12196a..5adfbb03aeb304 100644 --- a/components/gitpod-db/src/typeorm/user-storage-resources-db-impl.ts +++ b/components/gitpod-db/src/typeorm/user-storage-resources-db-impl.ts @@ -12,7 +12,6 @@ import { DBUserStorageResource } from "./entity/db-user-storage-resource"; @injectable() export class TypeORMUserStorageResourcesDBImpl implements UserStorageResourcesDB { - @inject(TypeORM) typeORM: TypeORM; protected async getEntityManager(): Promise { @@ -25,21 +24,24 @@ export class TypeORMUserStorageResourcesDBImpl implements UserStorageResourcesDB async get(userId: string, uri: string): Promise { const resource = await this.getResource(userId, uri); - const content = (resource) ? resource.content : ""; + const content = resource ? resource.content : ""; return content; } async update(userId: string, uri: string, content: string): Promise { // docs: https://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html const repo = await this.getUserStorageResourceRepo(); - await repo.query(` + await repo.query( + ` INSERT INTO d_b_user_storage_resource (userId, uri, content) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE content = VALUES(content); - `, [ userId, uri, content ]); + `, + [userId, uri, content], + ); } async deleteAllForUser(userId: string): Promise { @@ -49,10 +51,10 @@ export class TypeORMUserStorageResourcesDBImpl implements UserStorageResourcesDB protected async getResource(userId: string, uri: string): Promise { const repo = await this.getUserStorageResourceRepo(); - let query = repo.createQueryBuilder('resource') - .where('resource.uri = :uri AND resource.userId = :userId', { userId: userId, uri: uri }); + let query = repo + .createQueryBuilder("resource") + .where("resource.uri = :uri AND resource.userId = :userId", { userId: userId, uri: uri }); const resource = await query.getOne(); return resource; } - } diff --git a/components/gitpod-db/src/typeorm/workspace-cluster-db-impl.ts b/components/gitpod-db/src/typeorm/workspace-cluster-db-impl.ts index 4efdd9c349c65e..266abbbf681089 100644 --- a/components/gitpod-db/src/typeorm/workspace-cluster-db-impl.ts +++ b/components/gitpod-db/src/typeorm/workspace-cluster-db-impl.ts @@ -4,41 +4,43 @@ * See License-AGPL.txt in the project root for license information. */ - import { Repository, EntityManager, DeepPartial } from "typeorm"; - import { injectable, inject } from "inversify"; - import { TypeORM } from "./typeorm"; +import { Repository, EntityManager, DeepPartial } from "typeorm"; +import { injectable, inject } from "inversify"; +import { TypeORM } from "./typeorm"; import { WorkspaceClusterDB } from "../workspace-cluster-db"; import { DBWorkspaceCluster } from "./entity/db-workspace-cluster"; -import { WorkspaceCluster, WorkspaceClusterFilter, WorkspaceClusterWoTLS } from "@gitpod/gitpod-protocol/lib/workspace-cluster"; +import { + WorkspaceCluster, + WorkspaceClusterFilter, + WorkspaceClusterWoTLS, +} from "@gitpod/gitpod-protocol/lib/workspace-cluster"; - @injectable() - export class WorkspaceClusterDBImpl implements WorkspaceClusterDB { +@injectable() +export class WorkspaceClusterDBImpl implements WorkspaceClusterDB { + @inject(TypeORM) typeORM: TypeORM; - @inject(TypeORM) typeORM: TypeORM; - - protected async getEntityManager(): Promise { - return (await this.typeORM.getConnection()).manager; - } - - protected async getRepo(): Promise> { - return (await this.getEntityManager()).getRepository(DBWorkspaceCluster); - } + protected async getEntityManager(): Promise { + return (await this.typeORM.getConnection()).manager; + } - async save(cluster: WorkspaceCluster): Promise { - const repo = await this.getRepo(); - await repo.save(cluster); - } + protected async getRepo(): Promise> { + return (await this.getEntityManager()).getRepository(DBWorkspaceCluster); + } - async deleteByName(name: string): Promise { - const repo = await this.getRepo(); - await repo.delete(name); - } + async save(cluster: WorkspaceCluster): Promise { + const repo = await this.getRepo(); + await repo.save(cluster); + } - async findByName(name: string): Promise { - const repo = await this.getRepo(); - return repo.findOne(name); - } + async deleteByName(name: string): Promise { + const repo = await this.getRepo(); + await repo.delete(name); + } + async findByName(name: string): Promise { + const repo = await this.getRepo(); + return repo.findOne(name); + } async findFiltered(predicate: DeepPartial): Promise { const prototype: WorkspaceClusterWoTLS = { @@ -52,9 +54,10 @@ import { WorkspaceCluster, WorkspaceClusterFilter, WorkspaceClusterWoTLS } from }; const repo = await this.getRepo(); - let qb = repo.createQueryBuilder("wsc") - .select(Object.keys(prototype).map(k => `wsc.${k}`)) - .where("TRUE = TRUE"); // make sure andWhere works + let qb = repo + .createQueryBuilder("wsc") + .select(Object.keys(prototype).map((k) => `wsc.${k}`)) + .where("TRUE = TRUE"); // make sure andWhere works if (predicate.state !== undefined) { qb = qb.andWhere("wsc.state = :state", predicate); } @@ -69,4 +72,4 @@ import { WorkspaceCluster, WorkspaceClusterFilter, WorkspaceClusterWoTLS } from } return qb.getMany(); } - } +} diff --git a/components/gitpod-db/src/typeorm/workspace-db-impl.ts b/components/gitpod-db/src/typeorm/workspace-db-impl.ts index 50658604cbc5c1..17d950c75d3d3f 100644 --- a/components/gitpod-db/src/typeorm/workspace-db-impl.ts +++ b/components/gitpod-db/src/typeorm/workspace-db-impl.ts @@ -6,8 +6,36 @@ import { injectable, inject } from "inversify"; import { Repository, EntityManager, DeepPartial, UpdateQueryBuilder, Brackets } from "typeorm"; -import { MaybeWorkspace, MaybeWorkspaceInstance, WorkspaceDB, FindWorkspacesOptions, PrebuiltUpdatableAndWorkspace, WorkspaceInstanceSessionWithWorkspace, PrebuildWithWorkspace, WorkspaceAndOwner, WorkspacePortsAuthData, WorkspaceOwnerAndSoftDeleted } from "../workspace-db"; -import { Workspace, WorkspaceInstance, WorkspaceInfo, WorkspaceInstanceUser, WhitelistedRepository, Snapshot, LayoutData, PrebuiltWorkspace, RunningWorkspaceInfo, PrebuiltWorkspaceUpdatable, WorkspaceAndInstance, WorkspaceType, PrebuildInfo, AdminGetWorkspacesQuery, SnapshotState, PrebuiltWorkspaceState } from "@gitpod/gitpod-protocol"; +import { + MaybeWorkspace, + MaybeWorkspaceInstance, + WorkspaceDB, + FindWorkspacesOptions, + PrebuiltUpdatableAndWorkspace, + WorkspaceInstanceSessionWithWorkspace, + PrebuildWithWorkspace, + WorkspaceAndOwner, + WorkspacePortsAuthData, + WorkspaceOwnerAndSoftDeleted, +} from "../workspace-db"; +import { + Workspace, + WorkspaceInstance, + WorkspaceInfo, + WorkspaceInstanceUser, + WhitelistedRepository, + Snapshot, + LayoutData, + PrebuiltWorkspace, + RunningWorkspaceInfo, + PrebuiltWorkspaceUpdatable, + WorkspaceAndInstance, + WorkspaceType, + PrebuildInfo, + AdminGetWorkspacesQuery, + SnapshotState, + PrebuiltWorkspaceState, +} from "@gitpod/gitpod-protocol"; import { TypeORM } from "./typeorm"; import { DBWorkspace } from "./entity/db-workspace"; import { DBWorkspaceInstance } from "./entity/db-workspace-instance"; @@ -15,7 +43,7 @@ import { DBLayoutData } from "./entity/db-layout-data"; import { DBSnapshot } from "./entity/db-snapshot"; import { DBWorkspaceInstanceUser } from "./entity/db-workspace-instance-user"; import { DBRepositoryWhiteList } from "./entity/db-repository-whitelist"; -import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; +import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import { DBPrebuiltWorkspace } from "./entity/db-prebuilt-workspace"; import { DBPrebuiltWorkspaceUpdatable } from "./entity/db-prebuilt-workspace-updatable"; import { BUILTIN_WORKSPACE_PROBE_USER_ID } from "../user-db"; @@ -24,12 +52,11 @@ import { DBPrebuildInfo } from "./entity/db-prebuild-info-entry"; type RawTo = (instance: WorkspaceInstance, ws: Workspace) => T; interface OrderBy { fqField: string; - order: 'ASC' | 'DESC'; + order: "ASC" | "DESC"; } @injectable() export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { - protected abstract getManager(): Promise; protected async getWorkspaceRepo(): Promise> { @@ -61,7 +88,9 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { } protected async getPrebuiltWorkspaceUpdatableRepo(): Promise> { - return await (await this.getManager()).getRepository(DBPrebuiltWorkspaceUpdatable); + return await ( + await this.getManager() + ).getRepository(DBPrebuiltWorkspaceUpdatable); } protected async getLayoutDataRepo(): Promise> { @@ -76,7 +105,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { return; } catch (err) { log.error(`DB connection error (attempt ${tries} of ${maxTries})`, err); - await new Promise(resolve => setTimeout(resolve, timeout)); + await new Promise((resolve) => setTimeout(resolve, timeout)); } tries++; } @@ -93,8 +122,8 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { } public async findRunningInstance(workspaceId: string): Promise { - const instance = await this.findCurrentInstance(workspaceId) - if (instance && instance.status.phase !== 'stopped') { + const instance = await this.findCurrentInstance(workspaceId); + if (instance && instance.status.phase !== "stopped") { return instance; } return undefined; @@ -117,9 +146,12 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async findByInstanceId(instanceId: string): Promise { const workspaceRepo = await this.getWorkspaceRepo(); - const maybeRawWorkspaces = await workspaceRepo.query(`SELECT ws.* FROM d_b_workspace as ws + const maybeRawWorkspaces = (await workspaceRepo.query( + `SELECT ws.* FROM d_b_workspace as ws LEFT OUTER JOIN d_b_workspace_instance as wsi ON wsi.workspaceId = ws.id - WHERE wsi.id = ?;`, [instanceId]) as object[]; + WHERE wsi.id = ?;`, + [instanceId], + )) as object[]; if (!maybeRawWorkspaces || maybeRawWorkspaces.length !== 1) { return undefined; } @@ -140,22 +172,30 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { */ const repo = await this.getWorkspaceRepo(); const qb = repo - .createQueryBuilder('ws') + .createQueryBuilder("ws") // We need to put the subquery into the join condition (ON) here to be able to reference `ws.id` which is // not possible in a subquery on JOIN (e.g. 'LEFT JOIN (SELECT ... WHERE i.workspaceId = ws.id)') - .leftJoinAndMapOne('ws.latestInstance', DBWorkspaceInstance, 'wsi', - `wsi.id = (SELECT i.id FROM d_b_workspace_instance AS i WHERE i.workspaceId = ws.id ORDER BY i.creationTime DESC LIMIT 1)` + .leftJoinAndMapOne( + "ws.latestInstance", + DBWorkspaceInstance, + "wsi", + `wsi.id = (SELECT i.id FROM d_b_workspace_instance AS i WHERE i.workspaceId = ws.id ORDER BY i.creationTime DESC LIMIT 1)`, + ) + .leftJoin( + (qb) => { + return qb + .select("workspaceId") + .from(DBWorkspaceInstance, "i2") + .where('i2.phasePersisted = "running"'); + }, + "wsiRunning", + "ws.id = wsiRunning.workspaceId", ) - .leftJoin((qb) => { - return qb.select('workspaceId') - .from(DBWorkspaceInstance, 'i2') - .where('i2.phasePersisted = "running"'); - }, 'wsiRunning', 'ws.id = wsiRunning.workspaceId') - .where('ws.ownerId = :userId', { userId: options.userId }) - .andWhere('ws.softDeleted IS NULL') - .andWhere('ws.deleted != TRUE') - .orderBy('wsiRunning.workspaceId', 'DESC') - .addOrderBy('GREATEST(ws.creationTime, wsi.creationTime, wsi.startedTime, wsi.stoppedTime)', 'DESC') + .where("ws.ownerId = :userId", { userId: options.userId }) + .andWhere("ws.softDeleted IS NULL") + .andWhere("ws.deleted != TRUE") + .orderBy("wsiRunning.workspaceId", "DESC") + .addOrderBy("GREATEST(ws.creationTime, wsi.creationTime, wsi.startedTime, wsi.stoppedTime)", "DESC") .limit(options.limit || 10); if (options.searchString) { qb.andWhere("ws.description LIKE :searchString", { searchString: `%${options.searchString}%` }); @@ -166,33 +206,35 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { if (options.pinnedOnly) { qb.andWhere("ws.pinned = true"); } - const projectIds = typeof options.projectId === 'string' ? [options.projectId] : options.projectId; + const projectIds = typeof options.projectId === "string" ? [options.projectId] : options.projectId; if (projectIds !== undefined) { if (projectIds.length === 0 && !options.includeWithoutProject) { // user passed an empty array of projectids and also is not interested in unassigned workspaces -> no results return []; } - qb.andWhere(new Brackets(qb => { - // there is a schema mismatch: we use a transformer to map to empty string, but have a column-default of NULL. - // Thus all legacy workspaces (before the introduction of projectId) have a NULL in this column; all afterwards an empty string. - const emptyProjectId = "(ws.projectId IS NULL OR ws.projectId = '')"; - if (projectIds.length > 0) { - qb.where('ws.projectId IN (:pids)', { pids: projectIds }); - if (options.includeWithoutProject) { - qb.orWhere(emptyProjectId); + qb.andWhere( + new Brackets((qb) => { + // there is a schema mismatch: we use a transformer to map to empty string, but have a column-default of NULL. + // Thus all legacy workspaces (before the introduction of projectId) have a NULL in this column; all afterwards an empty string. + const emptyProjectId = "(ws.projectId IS NULL OR ws.projectId = '')"; + if (projectIds.length > 0) { + qb.where("ws.projectId IN (:pids)", { pids: projectIds }); + if (options.includeWithoutProject) { + qb.orWhere(emptyProjectId); + } + } else if (options.includeWithoutProject) { + qb.where(emptyProjectId); } - } else if (options.includeWithoutProject) { - qb.where(emptyProjectId); - } - })); + }), + ); } - const rawResults = await qb.getMany() as any as (Workspace & { latestInstance?: WorkspaceInstance })[]; // see leftJoinAndMapOne above - return rawResults.map(r => { + const rawResults = (await qb.getMany()) as any as (Workspace & { latestInstance?: WorkspaceInstance })[]; // see leftJoinAndMapOne above + return rawResults.map((r) => { const workspace = { ...r }; delete workspace.latestInstance; return { workspace, - latestInstance: r.latestInstance + latestInstance: r.latestInstance, }; }); } @@ -203,8 +245,8 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { ...raw, config: JSON.parse(raw.config), context: JSON.parse(raw.context), - pinned: raw.pinned && JSON.parse(raw.pinned) || undefined - } + pinned: (raw.pinned && JSON.parse(raw.pinned)) || undefined, + }; } protected async augmentWithCurrentInstance(workspaces: Workspace[]): Promise { @@ -213,33 +255,42 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { const latestInstance = await this.findCurrentInstance(workspace.id); result.push({ workspace, - latestInstance + latestInstance, }); } return result; } - public async updateLastHeartbeat(instanceId: string, userId: string, newHeartbeat: Date, wasClosed?: boolean): Promise { - const query = "INSERT INTO d_b_workspace_instance_user(instanceId, userId, lastSeen) VALUES (?, ?, timestamp ?) ON DUPLICATE KEY UPDATE lastSeen = timestamp ?, wasClosed = ?" + public async updateLastHeartbeat( + instanceId: string, + userId: string, + newHeartbeat: Date, + wasClosed?: boolean, + ): Promise { + const query = + "INSERT INTO d_b_workspace_instance_user(instanceId, userId, lastSeen) VALUES (?, ?, timestamp ?) ON DUPLICATE KEY UPDATE lastSeen = timestamp ?, wasClosed = ?"; const lastSeen = this.toTimestampString(newHeartbeat); const workspaceInstanceUserRepo = await this.getWorkspaceInstanceUserRepo(); workspaceInstanceUserRepo.query(query, [instanceId, userId, lastSeen, lastSeen, wasClosed || false]); } protected toTimestampString(date: Date) { - return date.toISOString().split('.')[0]; + return date.toISOString().split(".")[0]; } - public async getLastOwnerHeartbeatFor(instance: WorkspaceInstance): Promise<{ lastSeen: Date, wasClosed?: boolean } | undefined> { - const query = 'SELECT `DBWorkspaceInstanceUser`.`lastSeen` AS `lastSeen`,`DBWorkspaceInstanceUser`.`wasClosed` AS `wasClosed` FROM `d_b_workspace_instance_user` `DBWorkspaceInstanceUser` WHERE `DBWorkspaceInstanceUser`.`instanceId`=? AND `DBWorkspaceInstanceUser`.`userId`=(SELECT `ws`.`ownerId` AS `ws_ownerId` FROM `d_b_workspace` `ws` WHERE `ws`.`id` = ? LIMIT 1)' + public async getLastOwnerHeartbeatFor( + instance: WorkspaceInstance, + ): Promise<{ lastSeen: Date; wasClosed?: boolean } | undefined> { + const query = + "SELECT `DBWorkspaceInstanceUser`.`lastSeen` AS `lastSeen`,`DBWorkspaceInstanceUser`.`wasClosed` AS `wasClosed` FROM `d_b_workspace_instance_user` `DBWorkspaceInstanceUser` WHERE `DBWorkspaceInstanceUser`.`instanceId`=? AND `DBWorkspaceInstanceUser`.`userId`=(SELECT `ws`.`ownerId` AS `ws_ownerId` FROM `d_b_workspace` `ws` WHERE `ws`.`id` = ? LIMIT 1)"; const workspaceInstanceUserRepo = await this.getWorkspaceInstanceUserRepo(); const result = await workspaceInstanceUserRepo.query(query, [instance.id, instance.workspaceId]); if (result && result.length > 0 && result[0].lastSeen) { return { lastSeen: new Date(result[0].lastSeen), - wasClosed: Boolean(result[0].wasClosed) - } + wasClosed: Boolean(result[0].wasClosed), + }; } return undefined; } @@ -247,7 +298,8 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async getWorkspaceUsers(workspaceId: string, minLastSeen: number): Promise { const repo = await this.getWorkspaceInstanceUserRepo(); const minLastSeenString = this.toTimestampString(new Date(new Date().getTime() - minLastSeen)); - const query = "SELECT wsiu.instanceId as instanceId, wsiu.userId as userId, wsiu.lastSeen as lastSeen, user.avatarUrl as avatarUrl, user.name as name FROM d_b_workspace_instance_user wsiu, d_b_user user, d_b_workspace_instance wsi WHERE user.id = wsiu.userId AND wsi.id = wsiu.instanceId AND wsi.workspaceId = ? AND wsiu.lastSeen > (timestamp ?)"; + const query = + "SELECT wsiu.instanceId as instanceId, wsiu.userId as userId, wsiu.lastSeen as lastSeen, user.avatarUrl as avatarUrl, user.name as name FROM d_b_workspace_instance_user wsiu, d_b_user user, d_b_workspace_instance wsi WHERE user.id = wsiu.userId AND wsi.id = wsiu.instanceId AND wsi.workspaceId = ? AND wsiu.lastSeen > (timestamp ?)"; return repo.query(query, [workspaceId, minLastSeenString]); } @@ -258,7 +310,10 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { return await workspaceInstanceRepo.save(dbInstance); } - public async updateInstancePartial(instanceId: string, partial: DeepPartial): Promise { + public async updateInstancePartial( + instanceId: string, + partial: DeepPartial, + ): Promise { const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); if (!!partial.status) { (partial as any).phasePersisted = partial.status.phase; @@ -267,10 +322,13 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { return (await this.findInstanceById(instanceId))!; } - protected async queryUpdateInstanceConditional(instanceId: string, partial: DeepPartial): Promise> { + protected async queryUpdateInstanceConditional( + instanceId: string, + partial: DeepPartial, + ): Promise> { const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); - const qb = workspaceInstanceRepo.createQueryBuilder('wsi').update(); - return qb.set(partial).where('wsi.id = :instanceId', { instanceId }) + const qb = workspaceInstanceRepo.createQueryBuilder("wsi").update(); + return qb.set(partial).where("wsi.id = :instanceId", { instanceId }); } public async findInstanceById(workspaceInstanceId: string): Promise { @@ -280,9 +338,10 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async findInstances(workspaceId: string): Promise { const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); - const qBuilder = workspaceInstanceRepo.createQueryBuilder('wsi') - .where('wsi.workspaceId = :workspaceId', { workspaceId }) - .orderBy('creationTime', 'ASC'); + const qBuilder = workspaceInstanceRepo + .createQueryBuilder("wsi") + .where("wsi.workspaceId = :workspaceId", { workspaceId }) + .orderBy("creationTime", "ASC"); return qBuilder.getMany(); } @@ -293,9 +352,10 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async findCurrentInstance(workspaceId: string): Promise { const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); - const qb = workspaceInstanceRepo.createQueryBuilder('wsi') + const qb = workspaceInstanceRepo + .createQueryBuilder("wsi") .where(`wsi.workspaceId = :workspaceId`, { workspaceId }) - .orderBy('creationTime', 'DESC') + .orderBy("creationTime", "DESC") .limit(1); return qb.getOne(); } @@ -309,10 +369,11 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { minCreationTime?: Date, maxCreationTime?: Date, onlyRunning?: boolean, - type?: WorkspaceType - ): Promise<{ total: number, rows: WorkspaceInstance[] }> { + type?: WorkspaceType, + ): Promise<{ total: number; rows: WorkspaceInstance[] }> { const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); - const queryBuilder = workspaceInstanceRepo.createQueryBuilder("wsi") + const queryBuilder = workspaceInstanceRepo + .createQueryBuilder("wsi") .leftJoinAndMapOne("wsi.workspace", DBWorkspace, "ws", "wsi.workspaceId = ws.id") .skip(offset) .take(limit) @@ -322,10 +383,14 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { queryBuilder.andWhere("wsi.ownerId = :ownerId", { ownerId }); } if (minCreationTime) { - queryBuilder.andWhere("wsi.creationTime >= :minCreationTime", { minCreationTime: minCreationTime.toISOString() }); + queryBuilder.andWhere("wsi.creationTime >= :minCreationTime", { + minCreationTime: minCreationTime.toISOString(), + }); } if (maxCreationTime) { - queryBuilder.andWhere("wsi.creationTime < :maxCreationTime", { maxCreationTime: maxCreationTime.toISOString() }); + queryBuilder.andWhere("wsi.creationTime < :maxCreationTime", { + maxCreationTime: maxCreationTime.toISOString(), + }); } if (onlyRunning) { queryBuilder.andWhere("wsi.phasePersisted != 'stopped'").andWhere("wsi.deleted != TRUE"); @@ -336,7 +401,8 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async getInstanceCount(type?: string): Promise { const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); - const queryBuilder = workspaceInstanceRepo.createQueryBuilder("wsi") + const queryBuilder = workspaceInstanceRepo + .createQueryBuilder("wsi") .leftJoinAndMapOne("wsi.workspace", DBWorkspace, "ws", "wsi.workspaceId = ws.id") .where("ws.type = :type", { type: type ? type.toString() : "regular" }); // only regular workspaces by default @@ -345,12 +411,14 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async findRegularRunningInstances(userId?: string): Promise { const infos = await this.findRunningInstancesWithWorkspaces(undefined, userId); - return infos.filter( - info => info.workspace.type === 'regular' - ).map(wsinfo => wsinfo.latestInstance); + return infos.filter((info) => info.workspace.type === "regular").map((wsinfo) => wsinfo.latestInstance); } - public async findRunningInstancesWithWorkspaces(installation?: string, userId?: string, includeStopping: boolean = false): Promise { + public async findRunningInstancesWithWorkspaces( + installation?: string, + userId?: string, + includeStopping: boolean = false, + ): Promise { const params: any = {}; const conditions = ["wsi.phasePersisted != 'stopped'", "wsi.deleted != TRUE"]; if (!includeStopping) { @@ -367,14 +435,21 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { joinParams.userId = userId; joinConditions.push("ws.ownerId = :userId"); } - return this.doJoinInstanceWithWorkspace(conditions, params, joinConditions, joinParams, (wsi, ws) => { - return { workspace: ws, latestInstance: wsi }; - }) + return this.doJoinInstanceWithWorkspace( + conditions, + params, + joinConditions, + joinParams, + (wsi, ws) => { + return { workspace: ws, latestInstance: wsi }; + }, + ); } public async findWorkspacePortsAuthDataById(workspaceId: string): Promise { const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); - const results = await workspaceInstanceRepo.query(` + const results = (await workspaceInstanceRepo.query( + ` SELECT wsi.id AS wsi_id, wsi.region AS wsi_region, ws.id AS ws_id, @@ -386,9 +461,11 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { WHERE wsi.workspaceId = ? ORDER BY wsi.creationTime DESC LIMIT 1; - `, [workspaceId]) as any[]; + `, + [workspaceId], + )) as any[]; if (results.length < 1) { - return undefined + return undefined; } const res = results[0]; @@ -396,21 +473,26 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { workspace: { id: res.ws_id, ownerId: res.ws_ownerId, - shareable: res.ws_shareable + shareable: res.ws_shareable, }, instance: { id: res.wsi_id, - region: res.wsi_region - } + region: res.wsi_region, + }, }; } - public async findSessionsInPeriod(userId: string, periodStart: string, periodEnd: string): Promise { + public async findSessionsInPeriod( + userId: string, + periodStart: string, + periodEnd: string, + ): Promise { const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); // The query basically selects all workspace instances for the given owner, whose startDate is within the period, and which are either: // - not stopped yet, or // - is stopped or stopping. - const sessions = await workspaceInstanceRepo.query(` + const sessions = await workspaceInstanceRepo.query( + ` SELECT wsi.id AS wsi_id, wsi.startedTime AS wsi_startedTime, wsi.stoppedTime AS wsi_stoppedTime, @@ -425,7 +507,9 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { AND wsi.startedTime < ? AND (wsi.stoppedTime IS NULL OR wsi.stoppedTime = '' OR wsi.stoppedTime >= ? OR wsi.stoppingTime >= ?) ORDER BY wsi.creationTime ASC; - `, [userId, periodEnd, periodStart, periodStart]); + `, + [userId, periodEnd, periodStart, periodStart], + ); const resultSessions: WorkspaceInstanceSessionWithWorkspace[] = []; for (const session of sessions) { @@ -434,14 +518,14 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { id: session.ws_id, context: JSON.parse(session.ws_context), contextURL: session.ws_contextURL, - type: session.ws_type + type: session.ws_type, }, instance: { id: session.wsi_id, - startedTime: !session.wsi_startedTime ? undefined : session.wsi_startedTime, // Copy the TypeORM behavior according to column config - stoppedTime: !session.wsi_stoppedTime ? undefined : session.wsi_stoppedTime, // Copy the TypeORM behavior according to column config - stoppingTime: !session.wsi_stoppingTime ? undefined : session.wsi_stoppingTime // Copy the TypeORM behavior according to column config - } + startedTime: !session.wsi_startedTime ? undefined : session.wsi_startedTime, // Copy the TypeORM behavior according to column config + stoppedTime: !session.wsi_stoppedTime ? undefined : session.wsi_stoppedTime, // Copy the TypeORM behavior according to column config + stoppingTime: !session.wsi_stoppingTime ? undefined : session.wsi_stoppingTime, // Copy the TypeORM behavior according to column config + }, }); } return resultSessions; @@ -449,7 +533,8 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async findWorkspacesForGarbageCollection(minAgeInDays: number, limit: number): Promise { const workspaceRepo = await this.getWorkspaceRepo(); - const dbResults = await workspaceRepo.query(` + const dbResults = await workspaceRepo.query( + ` SELECT ws.id AS id, ws.ownerId AS ownerId FROM d_b_workspace AS ws @@ -462,14 +547,20 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { GROUP BY ws.id, ws.ownerId HAVING MAX(GREATEST(wsi.creationTime, wsi.startedTime, wsi.stoppedTime)) < NOW() - INTERVAL ? DAY OR MAX(wsi.creationTime) IS NULL LIMIT ?; - `, [minAgeInDays, minAgeInDays, limit]); + `, + [minAgeInDays, minAgeInDays, limit], + ); return dbResults as WorkspaceAndOwner[]; } - public async findWorkspacesForContentDeletion(minSoftDeletedTimeInDays: number, limit: number): Promise { + public async findWorkspacesForContentDeletion( + minSoftDeletedTimeInDays: number, + limit: number, + ): Promise { const workspaceRepo = await this.getWorkspaceRepo(); - const dbResults = await workspaceRepo.query(` + const dbResults = await workspaceRepo.query( + ` SELECT ws.id AS id, ws.ownerId AS ownerId, ws.softDeleted as softDeleted @@ -483,14 +574,17 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { ) AND ws.ownerId <> ? LIMIT ?; - `, [minSoftDeletedTimeInDays, BUILTIN_WORKSPACE_PROBE_USER_ID, limit]); + `, + [minSoftDeletedTimeInDays, BUILTIN_WORKSPACE_PROBE_USER_ID, limit], + ); return dbResults as WorkspaceOwnerAndSoftDeleted[]; } public async findPrebuiltWorkspacesForGC(daysUnused: number, limit: number): Promise { const workspaceRepo = await this.getWorkspaceRepo(); - const dbResults = await workspaceRepo.query(` + const dbResults = await workspaceRepo.query( + ` SELECT ws.id AS id, ws.ownerId AS ownerId FROM d_b_workspace AS ws, @@ -505,19 +599,28 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { HAVING max(usages.creationTime) IS NULL or max(usages.creationTime) < NOW() - INTERVAL ? DAY LIMIT ?; - `, [daysUnused, daysUnused, limit]); + `, + [daysUnused, daysUnused, limit], + ); return dbResults as WorkspaceAndOwner[]; } - protected async doJoinInstanceWithWorkspace(conditions: string[], conditionParams: {}, joinConditions: string[], joinConditionParams: {}, map: RawTo, orderBy?: OrderBy): Promise { + protected async doJoinInstanceWithWorkspace( + conditions: string[], + conditionParams: {}, + joinConditions: string[], + joinConditionParams: {}, + map: RawTo, + orderBy?: OrderBy, + ): Promise { type InstanceJoinResult = DBWorkspaceInstance & { workspace: Workspace }; - joinConditions = ['wsi.workspaceId = ws.id', ...joinConditions]; // Basic JOIN condition + joinConditions = ["wsi.workspaceId = ws.id", ...joinConditions]; // Basic JOIN condition const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo(); let qb = workspaceInstanceRepo - .createQueryBuilder('wsi') - .where(conditions.join(' AND '), conditionParams) - .innerJoinAndMapOne('wsi.workspace', DBWorkspace, 'ws', joinConditions.join(' AND '), joinConditionParams); + .createQueryBuilder("wsi") + .where(conditions.join(" AND "), conditionParams) + .innerJoinAndMapOne("wsi.workspace", DBWorkspace, "ws", joinConditions.join(" AND "), joinConditionParams); if (orderBy) { qb = qb.orderBy(orderBy.fqField, orderBy.order); } @@ -533,24 +636,26 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async isWhitelisted(repositoryUrl: string): Promise { const whitelist = await this.getRepositoryWhitelist(); - const repoCount = await whitelist.createQueryBuilder('rwl') - .select('1') - .where('rwl.url = :url', { 'url': repositoryUrl }) + const repoCount = await whitelist + .createQueryBuilder("rwl") + .select("1") + .where("rwl.url = :url", { url: repositoryUrl }) .getCount(); return repoCount > 0; } public async getFeaturedRepositories(): Promise[]> { const whitelist = await this.getRepositoryWhitelist(); - const allRepos = await whitelist.createQueryBuilder('rwl') - .where('rwl.priority >= :minPrio', { minPrio: DBRepositoryWhiteList.MIN_FEATURED_REPOSITORY_PRIO }) - .orderBy('priority', 'DESC') + const allRepos = await whitelist + .createQueryBuilder("rwl") + .where("rwl.priority >= :minPrio", { minPrio: DBRepositoryWhiteList.MIN_FEATURED_REPOSITORY_PRIO }) + .orderBy("priority", "DESC") .getMany(); - return allRepos.map(repo => { + return allRepos.map((repo) => { return { url: repo.url, - description: repo.description - } + description: repo.description, + }; }); } @@ -558,9 +663,14 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { const snapshots = await this.getSnapshotRepo(); return snapshots.findOne(snapshotId); } - public async findSnapshotsWithState(state: SnapshotState, offset: number, limit: number): Promise<{ snapshots: Snapshot[], total: number }> { + public async findSnapshotsWithState( + state: SnapshotState, + offset: number, + limit: number, + ): Promise<{ snapshots: Snapshot[]; total: number }> { const snapshotRepo = await this.getSnapshotRepo(); - const [snapshots, total] = await snapshotRepo.createQueryBuilder("snapshot") + const [snapshots, total] = await snapshotRepo + .createQueryBuilder("snapshot") .where("snapshot.state = :state", { state }) .orderBy("creationTime", "ASC") .offset(offset) @@ -580,7 +690,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { await snapshots.delete(snapshotId); } - public async updateSnapshot(snapshot: DeepPartial & Pick): Promise { + public async updateSnapshot(snapshot: DeepPartial & Pick): Promise { const snapshots = await this.getSnapshotRepo(); await snapshots.update(snapshot.id, snapshot); } @@ -593,29 +703,36 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async storePrebuiltWorkspace(pws: PrebuiltWorkspace): Promise { const repo = await this.getPrebuiltWorkspaceRepo(); if (pws.error && pws.error.length > 255) { - pws.error = pws.error.substring(0, 251) + " ..." + pws.error = pws.error.substring(0, 251) + " ..."; } return await repo.save(pws as DBPrebuiltWorkspace); } // Find the (last triggered) prebuild for a given commit - public async findPrebuiltWorkspaceByCommit(cloneURL: string, commit: string): Promise { + public async findPrebuiltWorkspaceByCommit( + cloneURL: string, + commit: string, + ): Promise { if (!commit || !cloneURL) { return undefined; } const repo = await this.getPrebuiltWorkspaceRepo(); - return await repo.createQueryBuilder('pws') - .where('pws.cloneURL = :cloneURL AND pws.commit LIKE :commit', { cloneURL, commit: commit + '%' }) - .orderBy('pws.creationTime', 'DESC') - .innerJoinAndMapOne('pws.workspace', DBWorkspace, 'ws', "pws.buildWorkspaceId = ws.id and ws.contentDeletedTime = ''") + return await repo + .createQueryBuilder("pws") + .where("pws.cloneURL = :cloneURL AND pws.commit LIKE :commit", { cloneURL, commit: commit + "%" }) + .orderBy("pws.creationTime", "DESC") + .innerJoinAndMapOne( + "pws.workspace", + DBWorkspace, + "ws", + "pws.buildWorkspaceId = ws.id and ws.contentDeletedTime = ''", + ) .getOne(); } public async findPrebuildByWorkspaceID(wsid: string): Promise { const repo = await this.getPrebuiltWorkspaceRepo(); - return await repo.createQueryBuilder('pws') - .where('pws.buildWorkspaceId = :wsid', { wsid }) - .getOne(); + return await repo.createQueryBuilder("pws").where("pws.buildWorkspaceId = :wsid", { wsid }).getOne(); } public async findPrebuildByID(pwsid: string): Promise { const repo = await this.getPrebuiltWorkspaceRepo(); @@ -623,7 +740,8 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { } public async countRunningPrebuilds(cloneURL: string): Promise { const repo = await this.getPrebuiltWorkspaceRepo(); - return await repo.createQueryBuilder('pws') + return await repo + .createQueryBuilder("pws") .where('pws.cloneURL = :cloneURL AND state = "building"', { cloneURL }) .getCount(); } @@ -631,51 +749,51 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async findPrebuildsWithWorkpace(cloneURL: string): Promise { const repo = await this.getPrebuiltWorkspaceRepo(); - let query = repo.createQueryBuilder('pws'); - query = query.where('pws.cloneURL = :cloneURL', { cloneURL }) - query = query.orderBy('pws.creationTime', 'DESC'); - query = query.innerJoinAndMapOne('pws.workspace', DBWorkspace, 'ws', 'pws.buildWorkspaceId = ws.id'); - query = query.where('ws.deleted = false'); + let query = repo.createQueryBuilder("pws"); + query = query.where("pws.cloneURL = :cloneURL", { cloneURL }); + query = query.orderBy("pws.creationTime", "DESC"); + query = query.innerJoinAndMapOne("pws.workspace", DBWorkspace, "ws", "pws.buildWorkspaceId = ws.id"); + query = query.where("ws.deleted = false"); const res = await query.getMany(); - return res.map(r => { + return res.map((r) => { const withWorkspace: PrebuiltWorkspace & { workspace: Workspace } = r as any; return { prebuild: r, workspace: withWorkspace.workspace, - } + }; }); } public async countUnabortedPrebuildsSince(cloneURL: string, date: Date): Promise { - const abortedState: PrebuiltWorkspaceState = 'aborted'; + const abortedState: PrebuiltWorkspaceState = "aborted"; const repo = await this.getPrebuiltWorkspaceRepo(); - let query = repo.createQueryBuilder('pws'); - query = query.where('pws.cloneURL = :cloneURL', { cloneURL }) - query = query.andWhere('pws.creationTime >= :time', {time: date.toISOString()}) - query = query.andWhere('pws.state != :state', { state: abortedState }) + let query = repo.createQueryBuilder("pws"); + query = query.where("pws.cloneURL = :cloneURL", { cloneURL }); + query = query.andWhere("pws.creationTime >= :time", { time: date.toISOString() }); + query = query.andWhere("pws.state != :state", { state: abortedState }); return query.getCount(); } public async findQueuedPrebuilds(cloneURL?: string): Promise { const repo = await this.getPrebuiltWorkspaceRepo(); - let query = await repo.createQueryBuilder('pws'); + let query = await repo.createQueryBuilder("pws"); query = query.where('state = "queued"'); if (cloneURL) { - query = query.andWhere('pws.cloneURL = :cloneURL', { cloneURL }) + query = query.andWhere("pws.cloneURL = :cloneURL", { cloneURL }); } - query = query.orderBy('pws.creationTime', 'ASC'); - query = query.innerJoinAndMapOne('pws.workspace', DBWorkspace, 'ws', 'pws.buildWorkspaceId = ws.id'); + query = query.orderBy("pws.creationTime", "ASC"); + query = query.innerJoinAndMapOne("pws.workspace", DBWorkspace, "ws", "pws.buildWorkspaceId = ws.id"); const res = await query.getMany(); - return res.map(r => { + return res.map((r) => { const withWorkspace: PrebuiltWorkspace & { workspace: Workspace } = r as any; return { prebuild: r, workspace: withWorkspace.workspace, - } + }; }); } public async attachUpdatableToPrebuild(pwsid: string, update: PrebuiltWorkspaceUpdatable): Promise { @@ -684,9 +802,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { } public async findUpdatablesForPrebuild(pwsid: string): Promise { const repo = await this.getPrebuiltWorkspaceUpdatableRepo(); - return await repo.createQueryBuilder('pwsu') - .where('pwsu.prebuiltWorkspaceId = :pwsid', { pwsid }) - .getMany(); + return await repo.createQueryBuilder("pwsu").where("pwsu.prebuiltWorkspaceId = :pwsid", { pwsid }).getMany(); } public async markUpdatableResolved(updatableId: string): Promise { const repo = await this.getPrebuiltWorkspaceUpdatableRepo(); @@ -696,12 +812,13 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { const pwsuRepo = await this.getPrebuiltWorkspaceUpdatableRepo(); // select * from d_b_prebuilt_workspace_updatable as pwsu left join d_b_prebuilt_workspace pws ON pws.id = pwsu.prebuiltWorkspaceId left join d_b_workspace ws on pws.buildWorkspaceId = ws.id left join d_b_workspace_instance wsi on ws.id = wsi.workspaceId where pwsu.isResolved = 0 - return await pwsuRepo.createQueryBuilder("pwsu") - .innerJoinAndMapOne('pwsu.prebuild', DBPrebuiltWorkspace, 'pws', 'pwsu.prebuiltWorkspaceId = pws.id') - .innerJoinAndMapOne('pwsu.workspace', DBWorkspace, 'ws', 'pws.buildWorkspaceId = ws.id') - .innerJoinAndMapOne('pwsu.instance', DBWorkspaceInstance, 'wsi', 'ws.id = wsi.workspaceId') - .where('pwsu.isResolved = 0') - .getMany() as any; + return (await pwsuRepo + .createQueryBuilder("pwsu") + .innerJoinAndMapOne("pwsu.prebuild", DBPrebuiltWorkspace, "pws", "pwsu.prebuiltWorkspaceId = pws.id") + .innerJoinAndMapOne("pwsu.workspace", DBWorkspace, "ws", "pws.buildWorkspaceId = ws.id") + .innerJoinAndMapOne("pwsu.instance", DBWorkspaceInstance, "wsi", "ws.id = wsi.workspaceId") + .where("pwsu.isResolved = 0") + .getMany()) as any; } public async findLayoutDataByWorkspaceId(workspaceId: string): Promise { @@ -734,10 +851,11 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { searchTerm?: string, minCreationTime?: Date, maxCreationTime?: Date, - type?: WorkspaceType - ): Promise<{ total: number, rows: Workspace[] }> { + type?: WorkspaceType, + ): Promise<{ total: number; rows: Workspace[] }> { const workspaceRepo = await this.getWorkspaceRepo(); - const queryBuilder = workspaceRepo.createQueryBuilder("ws") + const queryBuilder = workspaceRepo + .createQueryBuilder("ws") .skip(offset) .take(limit) .orderBy(orderBy, orderDir) @@ -749,10 +867,14 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { queryBuilder.andWhere("(contextURL LIKE :searchTerm OR description LIKE :searchTerm)", { searchTerm }); } if (minCreationTime) { - queryBuilder.andWhere("creationTime >= :minCreationTime", { minCreationTime: minCreationTime.toISOString() }); + queryBuilder.andWhere("creationTime >= :minCreationTime", { + minCreationTime: minCreationTime.toISOString(), + }); } if (maxCreationTime) { - queryBuilder.andWhere("creationTime < :maxCreationTime", { maxCreationTime: maxCreationTime.toISOString() }); + queryBuilder.andWhere("creationTime < :maxCreationTime", { + maxCreationTime: maxCreationTime.toISOString(), + }); } const [rows, total] = await queryBuilder.getManyAndCount(); return { total, rows }; @@ -760,13 +882,20 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { public async getWorkspaceCount(type?: String): Promise { const workspaceRepo = await this.getWorkspaceRepo(); - const queryBuilder = workspaceRepo.createQueryBuilder("ws") + const queryBuilder = workspaceRepo + .createQueryBuilder("ws") .where("ws.type = :type", { type: type ? type.toString() : "regular" }); // only regular workspaces by default return await queryBuilder.getCount(); } - public async findAllWorkspaceAndInstances(offset: number, limit: number, orderBy: keyof WorkspaceAndInstance, orderDir: "ASC" | "DESC", query?: AdminGetWorkspacesQuery): Promise<{ total: number, rows: WorkspaceAndInstance[] }> { + public async findAllWorkspaceAndInstances( + offset: number, + limit: number, + orderBy: keyof WorkspaceAndInstance, + orderDir: "ASC" | "DESC", + query?: AdminGetWorkspacesQuery, + ): Promise<{ total: number; rows: WorkspaceAndInstance[] }> { let whereConditions = []; let whereConditionParams: any = {}; let instanceIdQuery: boolean = false; @@ -796,33 +925,54 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { let orderField: string = orderBy; switch (orderField) { - case "workspaceId": orderField = "ws.id"; break; - case "instanceId": orderField = "wsi.id"; break; - case "contextURL": orderField = "ws.contextURL"; break; - case "workspaceCreationTime": orderField = "ws.creationTime"; break; - case "instanceCreationTime": orderField = "wsi.creationTime"; break; - case "phase": orderField = "wsi.status->>phase"; break; - case "ownerId": orderField = "wsi.ownerId"; break; + case "workspaceId": + orderField = "ws.id"; + break; + case "instanceId": + orderField = "wsi.id"; + break; + case "contextURL": + orderField = "ws.contextURL"; + break; + case "workspaceCreationTime": + orderField = "ws.creationTime"; + break; + case "instanceCreationTime": + orderField = "wsi.creationTime"; + break; + case "phase": + orderField = "wsi.status->>phase"; + break; + case "ownerId": + orderField = "wsi.ownerId"; + break; } // We need to select the latest wsi for a workspace. It's the same problem we have in 'find' (the "/workspaces" query, see above), so we use the same approach. // Only twist is that we might be searching for an instance directly ('instanceIdQuery'). const workspaceRepo = await this.getWorkspaceRepo(); let qb = workspaceRepo - .createQueryBuilder('ws') + .createQueryBuilder("ws") // We need to put the subquery into the join condition (ON) here to be able to reference `ws.id` which is // not possible in a subquery on JOIN (e.g. 'LEFT JOIN (SELECT ... WHERE i.workspaceId = ws.id)') - .leftJoinAndMapOne('ws.instance', DBWorkspaceInstance, 'wsi', - `${instanceIdQuery ? "wsi.workspaceId = ws.id" : "wsi.id = (SELECT i.id FROM d_b_workspace_instance AS i WHERE i.workspaceId = ws.id ORDER BY i.creationTime DESC LIMIT 1)"}` + .leftJoinAndMapOne( + "ws.instance", + DBWorkspaceInstance, + "wsi", + `${ + instanceIdQuery + ? "wsi.workspaceId = ws.id" + : "wsi.id = (SELECT i.id FROM d_b_workspace_instance AS i WHERE i.workspaceId = ws.id ORDER BY i.creationTime DESC LIMIT 1)" + }`, ) - .where(whereConditions.join(' AND '), whereConditionParams) + .where(whereConditions.join(" AND "), whereConditionParams) .orderBy(orderField, orderDir) .take(limit) .skip(offset); const rawResult = (await qb.getMany()) as InstanceJoinResult[]; const total = await qb.getCount(); - const rows = (rawResult as InstanceJoinResult[]).map(r => { + const rows = (rawResult as InstanceJoinResult[]).map((r) => { const res = { ...r, ...r.instance, @@ -839,7 +989,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { // @ts-ignore delete res["instance"]; - return (res); + return res; }); return { rows, total }; @@ -871,28 +1021,34 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { // @ts-ignore delete res["creationTime"]; - return (res); + return res; } async findInstancesByPhaseAndRegion(phase: string, region: string): Promise { const repo = await this.getWorkspaceInstanceRepo(); // uses index: ind_phasePersisted_region - const qb = repo.createQueryBuilder("wsi") + const qb = repo + .createQueryBuilder("wsi") .where("wsi.phasePersisted = :phase", { phase }) .andWhere("wsi.region = :region", { region }); return qb.getMany(); } - async findPrebuiltWorkspacesByProject(projectId: string, branch?: string, limit?: number): Promise { + async findPrebuiltWorkspacesByProject( + projectId: string, + branch?: string, + limit?: number, + ): Promise { const repo = await this.getPrebuiltWorkspaceRepo(); - const query = repo.createQueryBuilder('pws') - .orderBy('pws.creationTime', 'DESC') - .innerJoinAndMapOne('pws.workspace', DBWorkspace, 'ws', 'pws.buildWorkspaceId = ws.id') - .andWhere('pws.projectId = :projectId', { projectId }); + const query = repo + .createQueryBuilder("pws") + .orderBy("pws.creationTime", "DESC") + .innerJoinAndMapOne("pws.workspace", DBWorkspace, "ws", "pws.buildWorkspaceId = ws.id") + .andWhere("pws.projectId = :projectId", { projectId }); if (branch) { - query.andWhere('pws.branch = :branch', { branch }); + query.andWhere("pws.branch = :branch", { branch }); } if (limit) { query.limit(limit); @@ -905,10 +1061,11 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { async findPrebuiltWorkspaceById(id: string): Promise { const repo = await this.getPrebuiltWorkspaceRepo(); - const query = repo.createQueryBuilder('pws') - .orderBy('pws.creationTime', 'DESC') - .innerJoinAndMapOne('pws.workspace', DBWorkspace, 'ws', 'pws.buildWorkspaceId = ws.id') - .andWhere('pws.id = :id', { id }); + const query = repo + .createQueryBuilder("pws") + .orderBy("pws.creationTime", "DESC") + .innerJoinAndMapOne("pws.workspace", DBWorkspace, "ws", "pws.buildWorkspaceId = ws.id") + .andWhere("pws.id = :id", { id }); return query.getOne(); } @@ -917,30 +1074,28 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { const repo = await this.getPrebuildInfoRepo(); await repo.save({ prebuildId: prebuildInfo.id, - info: prebuildInfo + info: prebuildInfo, }); } async findPrebuildInfos(prebuildIds: string[]): Promise { const repo = await this.getPrebuildInfoRepo(); - const query = repo.createQueryBuilder('pi'); + const query = repo.createQueryBuilder("pi"); - const filteredIds = prebuildIds.filter(id => !!id); + const filteredIds = prebuildIds.filter((id) => !!id); if (filteredIds.length === 0) { return []; } - query.andWhere(`pi.prebuildId in (${filteredIds.map(id => `'${id}'`).join(", ")})`) + query.andWhere(`pi.prebuildId in (${filteredIds.map((id) => `'${id}'`).join(", ")})`); const res = await query.getMany(); - return res.map(r => r.info); + return res.map((r) => r.info); } - } @injectable() export class TypeORMWorkspaceDBImpl extends AbstractTypeORMWorkspaceDBImpl { - @inject(TypeORM) protected readonly typeorm: TypeORM; protected async getManager() { @@ -949,16 +1104,14 @@ export class TypeORMWorkspaceDBImpl extends AbstractTypeORMWorkspaceDBImpl { public async transaction(code: (db: WorkspaceDB) => Promise): Promise { const connection = await this.typeorm.getConnection(); - return connection.transaction(manager => { + return connection.transaction((manager) => { return code(new TransactionalWorkspaceDbImpl(manager)); }); } } export class TransactionalWorkspaceDbImpl extends AbstractTypeORMWorkspaceDBImpl { - - constructor( - protected readonly manager: EntityManager) { + constructor(protected readonly manager: EntityManager) { super(); } diff --git a/components/gitpod-db/src/user-db.spec.db.ts b/components/gitpod-db/src/user-db.spec.db.ts index 4ccd3668dd075e..9f16d935c42f91 100644 --- a/components/gitpod-db/src/user-db.spec.db.ts +++ b/components/gitpod-db/src/user-db.spec.db.ts @@ -4,20 +4,19 @@ * See License-AGPL.txt in the project root for license information. */ -import * as chai from 'chai'; +import * as chai from "chai"; const expect = chai.expect; -import { suite, test, timeout } from 'mocha-typescript'; - -import { Identity, Workspace, WorkspaceInstance } from '@gitpod/gitpod-protocol'; -import { testContainer } from './test-container'; -import { DBIdentity } from './typeorm/entity/db-identity'; -import { TypeORMUserDBImpl } from './typeorm/user-db-impl'; -import { TypeORMWorkspaceDBImpl } from './typeorm/workspace-db-impl'; -import { TypeORM } from './typeorm/typeorm'; -import { DBUser } from './typeorm/entity/db-user'; -import { DBWorkspace } from './typeorm/entity/db-workspace'; -import { DBWorkspaceInstance } from './typeorm/entity/db-workspace-instance'; - +import { suite, test, timeout } from "mocha-typescript"; + +import { Identity, Workspace, WorkspaceInstance } from "@gitpod/gitpod-protocol"; +import { testContainer } from "./test-container"; +import { DBIdentity } from "./typeorm/entity/db-identity"; +import { TypeORMUserDBImpl } from "./typeorm/user-db-impl"; +import { TypeORMWorkspaceDBImpl } from "./typeorm/workspace-db-impl"; +import { TypeORM } from "./typeorm/typeorm"; +import { DBUser } from "./typeorm/entity/db-user"; +import { DBWorkspace } from "./typeorm/entity/db-workspace"; +import { DBWorkspaceInstance } from "./typeorm/entity/db-workspace-instance"; const _IDENTITY1: Identity = { authProviderId: "GitHub", @@ -25,7 +24,7 @@ const _IDENTITY1: Identity = { authName: "gero", deleted: false, primaryEmail: undefined, - readonly: false + readonly: false, }; const _IDENTITY2: Identity = { authProviderId: "GitHub", @@ -33,12 +32,12 @@ const _IDENTITY2: Identity = { authName: "gero", deleted: false, primaryEmail: undefined, - readonly: false + readonly: false, }; -const WRONG_ID = '123'; // no uuid - -@suite class UserDBSpec { +const WRONG_ID = "123"; // no uuid +@suite +class UserDBSpec { db = testContainer.get(TypeORMUserDBImpl); wsDb = testContainer.get(TypeORMWorkspaceDBImpl); @@ -61,19 +60,23 @@ const WRONG_ID = '123'; // no uuid } // Copy to avoid pollution - get IDENTITY1() { return Object.assign({}, _IDENTITY1); } - get IDENTITY2() { return Object.assign({}, _IDENTITY2); } + get IDENTITY1() { + return Object.assign({}, _IDENTITY1); + } + get IDENTITY2() { + return Object.assign({}, _IDENTITY2); + } @test(timeout(10000)) public async createUserAndFindById() { let user = await this.db.newUser(); user.identities.push(this.IDENTITY1); - user = await this.db.storeUser(user) + user = await this.db.storeUser(user); - const dbResult = await this.db.findUserById(user.id) + const dbResult = await this.db.findUserById(user.id); // We use 'user' as reference, so clean it // @ts-ignore - user.identities.forEach(i => delete (i as DBIdentity).user); + user.identities.forEach((i) => delete (i as DBIdentity).user); expect(dbResult).to.deep.include(user); } @@ -89,12 +92,12 @@ const WRONG_ID = '123'; // no uuid public async createUserAndFindByIdentity() { let user = await this.db.newUser(); user.identities.push(this.IDENTITY1); - user = await this.db.storeUser(user) + user = await this.db.storeUser(user); const dbResult = await this.db.findUserByIdentity(this.IDENTITY1); // We use 'user' as reference, so clean it // @ts-ignore - user.identities.forEach(i => delete (i as DBIdentity).user); + user.identities.forEach((i) => delete (i as DBIdentity).user); expect(dbResult).to.deep.include(user); } @@ -105,22 +108,42 @@ const WRONG_ID = '123'; // no uuid user1.identities.push(TestData.ID1); user1.identities.push(TestData.ID2); user1.identities.push(TestData.ID3); - user1 = await this.db.storeUser(user1) - - await this.wsDb.store({ ...TestData.DEFAULT_WS, id: "1", creationTime: new Date().toISOString(), ownerId: user1.id }); - await this.wsDb.storeInstance({ ...TestData.DEFAULT_WSI, workspaceId: "1", id: "11", creationTime: new Date().toISOString() }) + user1 = await this.db.storeUser(user1); + + await this.wsDb.store({ + ...TestData.DEFAULT_WS, + id: "1", + creationTime: new Date().toISOString(), + ownerId: user1.id, + }); + await this.wsDb.storeInstance({ + ...TestData.DEFAULT_WSI, + workspaceId: "1", + id: "11", + creationTime: new Date().toISOString(), + }); // ensure that the second user's last modified is definitely after first one's - await new Promise(resolve => setTimeout(resolve, 100)); + await new Promise((resolve) => setTimeout(resolve, 100)); let user2 = await this.db.newUser(); user2.name = "New"; user2.identities.push(TestData.ID4); user2.identities.push(TestData.ID5); - user2 = await this.db.storeUser(user2) - - await this.wsDb.store({ ...TestData.DEFAULT_WS, id: "2", creationTime: new Date().toISOString(), ownerId: user2.id }); - await this.wsDb.storeInstance({ ...TestData.DEFAULT_WSI, workspaceId: "2", id: "22", creationTime: new Date().toISOString() }) + user2 = await this.db.storeUser(user2); + + await this.wsDb.store({ + ...TestData.DEFAULT_WS, + id: "2", + creationTime: new Date().toISOString(), + ownerId: user2.id, + }); + await this.wsDb.storeInstance({ + ...TestData.DEFAULT_WSI, + workspaceId: "2", + id: "22", + creationTime: new Date().toISOString(), + }); const dbResult = await this.db.findUsersByEmail(TestData.primaryEmail); @@ -136,14 +159,14 @@ const WRONG_ID = '123'; // no uuid user1.name = "ABC"; user1.identities.push(TestData.ID1); user1.identities.push(TestData.ID2); - user1 = await this.db.storeUser(user1) + user1 = await this.db.storeUser(user1); let user2 = await this.db.newUser(); user2.name = "XYZ"; user2.identities.push(TestData.ID3); - user2 = await this.db.storeUser(user2) + user2 = await this.db.storeUser(user2); user2.identities.push(TestData.ID2); - user2 = await this.db.storeUser(user2) + user2 = await this.db.storeUser(user2); const r2 = await this.db.findUserByIdentity(TestData.ID1); expect(r2).to.be.not.undefined; @@ -163,7 +186,7 @@ namespace TestData { authId: "1234", authName: "Foo Bar", deleted: false, - readonly: false + readonly: false, }; export const ID1: Identity = { ...DEFAULT, authId: "2345" }; export const ID2: Identity = { ...DEFAULT, authId: "3456", authProviderId: "Public-GitLab" }; @@ -171,25 +194,25 @@ namespace TestData { export const ID4: Identity = { ...DEFAULT, authId: "5678" }; export const ID5: Identity = { ...DEFAULT, authId: "6789", authProviderId: "ACME" }; export const DEFAULT_WS: Workspace = { - id: '1', - type: 'regular', + id: "1", + type: "regular", creationTime: new Date().toISOString(), config: { ports: [], - image: '', - tasks: [] + image: "", + tasks: [], }, - context: { title: 'example' }, - contextURL: 'example.org', - description: 'blabla', - ownerId: '12345' + context: { title: "example" }, + contextURL: "example.org", + description: "blabla", + ownerId: "12345", }; export const DEFAULT_WSI: WorkspaceInstance = { workspaceId: DEFAULT_WS.id, - id: '123', - ideUrl: 'example.org', - region: 'unknown', - workspaceImage: 'abc.io/test/image:123', + id: "123", + ideUrl: "example.org", + region: "unknown", + workspaceImage: "abc.io/test/image:123", creationTime: new Date().toISOString(), startedTime: undefined, deployedTime: undefined, @@ -200,10 +223,10 @@ namespace TestData { }, configuration: { theiaVersion: "unknown", - ideImage: "unknown" + ideImage: "unknown", }, - deleted: false + deleted: false, }; } -module.exports = new UserDBSpec() +module.exports = new UserDBSpec(); diff --git a/components/gitpod-db/src/user-db.ts b/components/gitpod-db/src/user-db.ts index f0e41f4069b50a..5362f20e0fc0e2 100644 --- a/components/gitpod-db/src/user-db.ts +++ b/components/gitpod-db/src/user-db.ts @@ -4,14 +4,24 @@ * See License-AGPL.txt in the project root for license information. */ -import { AdditionalUserData, GitpodToken, GitpodTokenType, Identity, IdentityLookup, Token, TokenEntry, User, UserEnvVar } from "@gitpod/gitpod-protocol"; +import { + AdditionalUserData, + GitpodToken, + GitpodTokenType, + Identity, + IdentityLookup, + Token, + TokenEntry, + User, + UserEnvVar, +} from "@gitpod/gitpod-protocol"; import { OAuthTokenRepository, OAuthUserRepository } from "@jmondi/oauth2-server"; import { Repository } from "typeorm"; import { DBUser } from "./typeorm/entity/db-user"; export type MaybeUser = User | undefined; -export const UserDB = Symbol('UserDB'); +export const UserDB = Symbol("UserDB"); export interface UserDB extends OAuthUserRepository, OAuthTokenRepository { transaction(code: (db: UserDB) => Promise): Promise; @@ -20,7 +30,7 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository { updateUserPartial(partial: PartialUserUpdate): Promise; findUserById(id: string): Promise; findUserByIdentity(identity: IdentityLookup): Promise; - findIdentitiesByName(identity: Pick): Promise; + findIdentitiesByName(identity: Pick): Promise; /** * Gets the number of users. @@ -37,7 +47,7 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository { * @param identity * @param token */ - storeSingleToken(identity: Pick, token: Token): Promise; + storeSingleToken(identity: Pick, token: Token): Promise; /** * adds the given token to the identity @@ -45,7 +55,7 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository { * @param identity * @param token */ - addToken(identity: Pick, token: Token): Promise; + addToken(identity: Pick, token: Token): Promise; /** * Will mark tokens for the given identity as deleted. @@ -53,7 +63,7 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository { * @param identity * @param shouldDelete optional predicate to suppress deletion of certain entries */ - deleteTokens(identity: Identity, shouldDelete?: (entry: TokenEntry) => boolean): Promise + deleteTokens(identity: Identity, shouldDelete?: (entry: TokenEntry) => boolean): Promise; /** * Find TokenEntry by id @@ -81,7 +91,7 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository { * * @param tokenEntry */ - updateTokenEntry(tokenEntry: Partial & Pick): Promise + updateTokenEntry(tokenEntry: Partial & Pick): Promise; /** * @param identity @@ -107,25 +117,37 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository { deleteEnvVar(envVar: UserEnvVar): Promise; getEnvVars(userId: string): Promise; - findAllUsers(offset: number, limit: number, orderBy: keyof User, orderDir: "ASC" | "DESC", searchTerm?: string, minCreationDate?: Date, maxCreationDate?: Date, excludeBuiltinUsers?: boolean): Promise<{ total: number, rows: User[] }>; + findAllUsers( + offset: number, + limit: number, + orderBy: keyof User, + orderDir: "ASC" | "DESC", + searchTerm?: string, + minCreationDate?: Date, + maxCreationDate?: Date, + excludeBuiltinUsers?: boolean, + ): Promise<{ total: number; rows: User[] }>; findUserByName(name: string): Promise; - findUserByGitpodToken(tokenHash: string, tokenType?: GitpodTokenType): Promise<{ user: User, token: GitpodToken } | undefined>; + findUserByGitpodToken( + tokenHash: string, + tokenType?: GitpodTokenType, + ): Promise<{ user: User; token: GitpodToken } | undefined>; findGitpodTokensOfUser(userId: string, tokenHash: string): Promise; findAllGitpodTokensOfUser(userId: string): Promise; storeGitpodToken(token: GitpodToken & { user: DBUser }): Promise; deleteGitpodToken(tokenHash: string): Promise; deleteGitpodTokensNamedLike(userId: string, namePattern: string): Promise; } -export type PartialUserUpdate = Partial> & Pick +export type PartialUserUpdate = Partial> & Pick; export const BUILTIN_WORKSPACE_PROBE_USER_ID = "builtin-user-workspace-probe-0000000"; export interface OwnerAndRepo { - owner: string - repo: string + owner: string; + repo: string; } -export type UserEmailContact = Pick - & { primaryEmail: string } - & { additionalData?: Pick } +export type UserEmailContact = Pick & { primaryEmail: string } & { + additionalData?: Pick; +}; diff --git a/components/gitpod-db/src/user-message-views-db.spec.db.ts b/components/gitpod-db/src/user-message-views-db.spec.db.ts index a43557b10f4f95..891fd86a09d41d 100644 --- a/components/gitpod-db/src/user-message-views-db.spec.db.ts +++ b/components/gitpod-db/src/user-message-views-db.spec.db.ts @@ -4,21 +4,18 @@ * See License-AGPL.txt in the project root for license information. */ - -import * as chai from 'chai'; +import * as chai from "chai"; const expect = chai.expect; -import { suite, test, timeout } from 'mocha-typescript'; - -import { testContainer } from './test-container'; -import { TypeORM } from './typeorm/typeorm'; -import { DBUserMessageViewEntry } from './typeorm/entity/db-user-message-view-entry'; -import { Repository } from 'typeorm'; -import { UserMessageViewsDB } from './user-message-views-db'; +import { suite, test, timeout } from "mocha-typescript"; +import { testContainer } from "./test-container"; +import { TypeORM } from "./typeorm/typeorm"; +import { DBUserMessageViewEntry } from "./typeorm/entity/db-user-message-view-entry"; +import { Repository } from "typeorm"; +import { UserMessageViewsDB } from "./user-message-views-db"; @suite class UserMessageViewsDBSpec { - typeORM = testContainer.get(TypeORM); viewsdb = testContainer.get(UserMessageViewsDB); @@ -41,16 +38,16 @@ class UserMessageViewsDBSpec { @test(timeout(10000)) public async testSimple11() { - const viewed = await this.viewsdb.didViewMessage('user1', 'message1'); + const viewed = await this.viewsdb.didViewMessage("user1", "message1"); expect(viewed).to.be.false; } @test(timeout(10000)) public async testSimple2() { - await this.viewsdb.markAsViewed('user1', ['message1']); - const viewed = await this.viewsdb.didViewMessage('user1', 'message1'); + await this.viewsdb.markAsViewed("user1", ["message1"]); + const viewed = await this.viewsdb.didViewMessage("user1", "message1"); expect(viewed).to.be.true; } } -module.exports = new UserMessageViewsDBSpec() +module.exports = new UserMessageViewsDBSpec(); diff --git a/components/gitpod-db/src/user-message-views-db.ts b/components/gitpod-db/src/user-message-views-db.ts index 066411428f5a04..34b77be7c32c83 100644 --- a/components/gitpod-db/src/user-message-views-db.ts +++ b/components/gitpod-db/src/user-message-views-db.ts @@ -4,8 +4,7 @@ * See License-AGPL.txt in the project root for license information. */ - -export const UserMessageViewsDB = Symbol('UserMessageViewsDB'); +export const UserMessageViewsDB = Symbol("UserMessageViewsDB"); export interface UserMessageViewsDB { didViewMessage(userId: string, messageId: string): Promise; diff --git a/components/gitpod-db/src/user-storage-resources-db.ts b/components/gitpod-db/src/user-storage-resources-db.ts index 3b85f089be010d..10bfcde2f89762 100644 --- a/components/gitpod-db/src/user-storage-resources-db.ts +++ b/components/gitpod-db/src/user-storage-resources-db.ts @@ -4,8 +4,7 @@ * See License-AGPL.txt in the project root for license information. */ - -export const UserStorageResourcesDB = Symbol('UserStorageResourcesDB'); +export const UserStorageResourcesDB = Symbol("UserStorageResourcesDB"); export interface UserStorageResourcesDB { get(userId: string, uri: string): Promise; diff --git a/components/gitpod-db/src/user-storage-resources-spec.db.ts b/components/gitpod-db/src/user-storage-resources-spec.db.ts index a3b6cec4cc812c..dd8debe89cad3c 100644 --- a/components/gitpod-db/src/user-storage-resources-spec.db.ts +++ b/components/gitpod-db/src/user-storage-resources-spec.db.ts @@ -4,21 +4,18 @@ * See License-AGPL.txt in the project root for license information. */ - -import * as chai from 'chai'; +import * as chai from "chai"; const expect = chai.expect; -import { suite, test, timeout } from 'mocha-typescript'; - -import { testContainer } from './test-container'; -import { TypeORM } from './typeorm/typeorm'; -import { Repository } from 'typeorm'; -import { UserStorageResourcesDB } from './user-storage-resources-db'; -import { DBUserStorageResource } from './typeorm/entity/db-user-storage-resource'; +import { suite, test, timeout } from "mocha-typescript"; +import { testContainer } from "./test-container"; +import { TypeORM } from "./typeorm/typeorm"; +import { Repository } from "typeorm"; +import { UserStorageResourcesDB } from "./user-storage-resources-db"; +import { DBUserStorageResource } from "./typeorm/entity/db-user-storage-resource"; @suite class UserStorageResourcesDBSpec { - typeORM = testContainer.get(TypeORM); resourcesDb = testContainer.get(UserStorageResourcesDB); @@ -33,16 +30,16 @@ class UserStorageResourcesDBSpec { @test(timeout(10000)) public async testGetEmpty() { - const content = await this.resourcesDb.get('user1', 'some://uri'); - expect(content).to.be.eq(''); + const content = await this.resourcesDb.get("user1", "some://uri"); + expect(content).to.be.eq(""); } @test(timeout(10000)) public async testUpdate() { - await this.resourcesDb.update('user1', 'some://uri', 'content'); - const content = await this.resourcesDb.get('user1', 'some://uri'); - expect(content).to.be.eq('content'); + await this.resourcesDb.update("user1", "some://uri", "content"); + const content = await this.resourcesDb.get("user1", "some://uri"); + expect(content).to.be.eq("content"); } } -module.exports = new UserStorageResourcesDBSpec() +module.exports = new UserStorageResourcesDBSpec(); diff --git a/components/gitpod-db/src/wait-for-db.ts b/components/gitpod-db/src/wait-for-db.ts index b7a1fc7d523c32..84cab25aeeb061 100644 --- a/components/gitpod-db/src/wait-for-db.ts +++ b/components/gitpod-db/src/wait-for-db.ts @@ -9,13 +9,13 @@ */ import "reflect-metadata"; import { Config } from "./config"; -import * as mysql from 'mysql'; +import * as mysql from "mysql"; const retryPeriod = 5000; // [ms] const totalAttempts = 30; const connCfg = { ...new Config().mysqlConfig, - timeout: retryPeriod + timeout: retryPeriod, }; function connectOrReschedule(attempt: number) { @@ -30,15 +30,14 @@ function connectOrReschedule(attempt: number) { process.exit(0); } }); - } catch(err) { + } catch (err) { rescheduleConnectionAttempt(attempt, err); } - } function rescheduleConnectionAttempt(attempt: number, err: Error) { - if(attempt == totalAttempts) { - console.log(`Could not connect within ${totalAttempts} attempts. Stopping.`) + if (attempt == totalAttempts) { + console.log(`Could not connect within ${totalAttempts} attempts. Stopping.`); process.exit(1); } console.log(`Connection attempt ${attempt}/${totalAttempts} failed. Retrying in ${retryPeriod / 1000} seconds.`); diff --git a/components/gitpod-db/src/workspace-cluster-db.ts b/components/gitpod-db/src/workspace-cluster-db.ts index 8a048e031df3b0..978212932e9457 100644 --- a/components/gitpod-db/src/workspace-cluster-db.ts +++ b/components/gitpod-db/src/workspace-cluster-db.ts @@ -8,6 +8,4 @@ import * as protocol from "@gitpod/gitpod-protocol/lib/workspace-cluster"; // This interface lives in protocol due to dependency issues but is re-exported here for consistency. export const WorkspaceClusterDB = protocol.WorkspaceClusterDB; -export interface WorkspaceClusterDB extends protocol.WorkspaceClusterDB { - -} \ No newline at end of file +export interface WorkspaceClusterDB extends protocol.WorkspaceClusterDB {} diff --git a/components/gitpod-db/src/workspace-db.spec.db.ts b/components/gitpod-db/src/workspace-db.spec.db.ts index b6407c3a3b6a95..68d571aa00479a 100644 --- a/components/gitpod-db/src/workspace-db.spec.db.ts +++ b/components/gitpod-db/src/workspace-db.spec.db.ts @@ -4,52 +4,52 @@ * See License-AGPL.txt in the project root for license information. */ -import * as chai from 'chai'; +import * as chai from "chai"; const expect = chai.expect; -import { suite, test, timeout } from 'mocha-typescript'; -import { fail } from 'assert'; - -import { WorkspaceInstance, Workspace, PrebuiltWorkspace } from '@gitpod/gitpod-protocol'; -import { testContainer } from './test-container'; -import { TypeORMWorkspaceDBImpl } from './typeorm/workspace-db-impl'; -import { TypeORM } from './typeorm/typeorm'; -import { DBWorkspace } from './typeorm/entity/db-workspace'; -import { DBPrebuiltWorkspace } from './typeorm/entity/db-prebuilt-workspace'; -import { DBWorkspaceInstance } from './typeorm/entity/db-workspace-instance'; -import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; - -@suite class WorkspaceDBSpec { - +import { suite, test, timeout } from "mocha-typescript"; +import { fail } from "assert"; + +import { WorkspaceInstance, Workspace, PrebuiltWorkspace } from "@gitpod/gitpod-protocol"; +import { testContainer } from "./test-container"; +import { TypeORMWorkspaceDBImpl } from "./typeorm/workspace-db-impl"; +import { TypeORM } from "./typeorm/typeorm"; +import { DBWorkspace } from "./typeorm/entity/db-workspace"; +import { DBPrebuiltWorkspace } from "./typeorm/entity/db-prebuilt-workspace"; +import { DBWorkspaceInstance } from "./typeorm/entity/db-workspace-instance"; +import { secondsBefore } from "@gitpod/gitpod-protocol/lib/util/timeutil"; + +@suite +class WorkspaceDBSpec { db = testContainer.get(TypeORMWorkspaceDBImpl); typeorm = testContainer.get(TypeORM); readonly timeWs = new Date(2018, 2, 16, 10, 0, 0).toISOString(); readonly timeBefore = new Date(2018, 2, 16, 11, 5, 10).toISOString(); readonly timeAfter = new Date(2019, 2, 16, 11, 5, 10).toISOString(); - readonly userId = '12345'; - readonly projectAID = 'projectA'; - readonly projectBID = 'projectB'; + readonly userId = "12345"; + readonly projectAID = "projectA"; + readonly projectBID = "projectB"; readonly ws: Workspace = { - id: '1', - type: 'regular', + id: "1", + type: "regular", creationTime: this.timeWs, config: { ports: [], - image: '', - tasks: [] + image: "", + tasks: [], }, projectId: this.projectAID, - context: { title: 'example' }, - contextURL: 'example.org', - description: 'blabla', - ownerId: this.userId + context: { title: "example" }, + contextURL: "example.org", + description: "blabla", + ownerId: this.userId, }; readonly wsi1: WorkspaceInstance = { workspaceId: this.ws.id, - id: '123', - ideUrl: 'example.org', - region: 'unknown', - workspaceImage: 'abc.io/test/image:123', + id: "123", + ideUrl: "example.org", + region: "unknown", + workspaceImage: "abc.io/test/image:123", creationTime: this.timeBefore, startedTime: undefined, deployedTime: undefined, @@ -61,16 +61,16 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; }, configuration: { theiaVersion: "unknown", - ideImage: "unknown" + ideImage: "unknown", }, - deleted: false + deleted: false, }; readonly wsi2: WorkspaceInstance = { workspaceId: this.ws.id, - id: '1234', - ideUrl: 'example.org', - region: 'unknown', - workspaceImage: 'abc.io/test/image:123', + id: "1234", + ideUrl: "example.org", + region: "unknown", + workspaceImage: "abc.io/test/image:123", creationTime: this.timeAfter, startedTime: undefined, deployedTime: undefined, @@ -82,31 +82,31 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; }, configuration: { theiaVersion: "unknown", - ideImage: "unknown" + ideImage: "unknown", }, - deleted: false + deleted: false, }; readonly ws2: Workspace = { - id: '2', - type: 'regular', + id: "2", + type: "regular", creationTime: this.timeWs, config: { ports: [], - image: '', - tasks: [] + image: "", + tasks: [], }, projectId: this.projectBID, - context: { title: 'example' }, - contextURL: 'https://github.com/gitpod-io/gitpod', - description: 'Gitpod', - ownerId: this.userId + context: { title: "example" }, + contextURL: "https://github.com/gitpod-io/gitpod", + description: "Gitpod", + ownerId: this.userId, }; readonly ws2i1: WorkspaceInstance = { workspaceId: this.ws2.id, - id: '4', - ideUrl: 'example.org', - region: 'unknown', - workspaceImage: 'abc.io/test/image:123', + id: "4", + ideUrl: "example.org", + region: "unknown", + workspaceImage: "abc.io/test/image:123", creationTime: this.timeBefore, startedTime: undefined, deployedTime: undefined, @@ -118,31 +118,31 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; }, configuration: { theiaVersion: "unknown", - ideImage: "unknown" + ideImage: "unknown", }, - deleted: false + deleted: false, }; readonly ws3: Workspace = { - id: '3', - type: 'regular', + id: "3", + type: "regular", creationTime: this.timeWs, config: { ports: [], - image: '', - tasks: [] + image: "", + tasks: [], }, - context: { title: 'example' }, - contextURL: 'example.org', - description: 'blabla', - ownerId: this.userId + context: { title: "example" }, + contextURL: "example.org", + description: "blabla", + ownerId: this.userId, }; readonly ws3i1: WorkspaceInstance = { workspaceId: this.ws3.id, - id: '3_1', - ideUrl: 'example.org', - region: 'unknown', - workspaceImage: 'abc.io/test/image:123', + id: "3_1", + ideUrl: "example.org", + region: "unknown", + workspaceImage: "abc.io/test/image:123", creationTime: this.timeBefore, startedTime: undefined, deployedTime: undefined, @@ -154,9 +154,9 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; }, configuration: { theiaVersion: "unknown", - ideImage: "unknown" + ideImage: "unknown", }, - deleted: false + deleted: false, }; async before() { @@ -168,7 +168,7 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; } async wipeRepo() { - const mnr = await (this.typeorm.getConnection()); + const mnr = await this.typeorm.getConnection(); await mnr.getRepository(DBWorkspace).delete({}); await mnr.getRepository(DBWorkspaceInstance).delete({}); await mnr.getRepository(DBPrebuiltWorkspace).delete({}); @@ -177,24 +177,19 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; @test(timeout(10000)) public async testFindInstancesLast() { try { - await this.db.transaction(async db => { - await Promise.all([ - db.store(this.ws), - db.storeInstance(this.wsi1), - db.storeInstance(this.wsi2) - ]); + await this.db.transaction(async (db) => { + await Promise.all([db.store(this.ws), db.storeInstance(this.wsi1), db.storeInstance(this.wsi2)]); const dbResult = await db.findInstances(this.ws.id); expect(dbResult).to.have.deep.members([this.wsi1, this.wsi2]); - throw 'rollback'; - }) + throw "rollback"; + }); } catch (e) { - if (e !== 'rollback') - throw e; + if (e !== "rollback") throw e; const dbResult = await this.db.findInstances(this.ws.id); expect(dbResult).to.not.have.deep.members([this.wsi1, this.wsi2]); return; } - fail('Rollback failed') + fail("Rollback failed"); } @test(timeout(10000)) @@ -202,8 +197,8 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; await this.createPrebuild(2); const dbResult = await this.db.findPrebuiltWorkspacesForGC(1, 10); expect(dbResult.length).to.eq(1); - expect(dbResult[0].id).to.eq('12345'); - expect(dbResult[0].ownerId).to.eq('1221423'); + expect(dbResult[0].id).to.eq("12345"); + expect(dbResult[0].ownerId).to.eq("1221423"); } @test(timeout(10000)) @@ -218,8 +213,8 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; await this.createPrebuild(2, 2); const dbResult = await this.db.findPrebuiltWorkspacesForGC(1, 10); expect(dbResult.length).to.eq(1); - expect(dbResult[0].id).to.eq('12345'); - expect(dbResult[0].ownerId).to.eq('1221423'); + expect(dbResult[0].id).to.eq("12345"); + expect(dbResult[0].ownerId).to.eq("1221423"); } @test(timeout(10000)) @@ -234,51 +229,47 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; now.setDate(now.getDate() - createdDaysAgo); const creationTime = now.toISOString(); await this.db.store({ - id: '12345', + id: "12345", creationTime, - description: 'something', - contextURL: 'https://github.com/foo/bar', - ownerId: '1221423', + description: "something", + contextURL: "https://github.com/foo/bar", + ownerId: "1221423", context: { - title: 'my title' + title: "my title", }, config: {}, - type: 'prebuild' + type: "prebuild", }); await this.db.storePrebuiltWorkspace({ - id: 'prebuild123', - buildWorkspaceId: '12345', + id: "prebuild123", + buildWorkspaceId: "12345", creationTime, - cloneURL: '', - commit: '', - state: 'available' + cloneURL: "", + commit: "", + state: "available", }); if (usageDaysAgo !== undefined) { const now = new Date(); now.setDate(now.getDate() - usageDaysAgo); await this.db.store({ - id: 'usage-of-12345', + id: "usage-of-12345", creationTime: now.toISOString(), - description: 'something', - contextURL: 'https://github.com/foo/bar', - ownerId: '1221423', + description: "something", + contextURL: "https://github.com/foo/bar", + ownerId: "1221423", context: { - title: 'my title' + title: "my title", }, config: {}, - basedOnPrebuildId: 'prebuild123', - type: 'regular' + basedOnPrebuildId: "prebuild123", + type: "regular", }); } } @test(timeout(10000)) public async testFindWorkspacesForGarbageCollection() { - await Promise.all([ - this.db.store(this.ws), - this.db.storeInstance(this.wsi1), - this.db.storeInstance(this.wsi2) - ]); + await Promise.all([this.db.store(this.ws), this.db.storeInstance(this.wsi1), this.db.storeInstance(this.wsi2)]); const dbResult = await this.db.findWorkspacesForGarbageCollection(14, 10); expect(dbResult[0].id).to.eq(this.ws.id); expect(dbResult[0].ownerId).to.eq(this.ws.ownerId); @@ -286,9 +277,7 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; @test(timeout(10000)) public async testFindWorkspacesForGarbageCollection_no_instance() { - await Promise.all([ - this.db.store(this.ws) - ]); + await Promise.all([this.db.store(this.ws)]); const dbResult = await this.db.findWorkspacesForGarbageCollection(14, 10); expect(dbResult[0].id).to.eq(this.ws.id); expect(dbResult[0].ownerId).to.eq(this.ws.ownerId); @@ -297,20 +286,14 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; @test(timeout(10000)) public async testFindWorkspacesForGarbageCollection_latelyUsed() { this.wsi2.creationTime = new Date().toISOString(); - await Promise.all([ - this.db.store(this.ws), - this.db.storeInstance(this.wsi1), - this.db.storeInstance(this.wsi2) - ]); + await Promise.all([this.db.store(this.ws), this.db.storeInstance(this.wsi1), this.db.storeInstance(this.wsi2)]); const dbResult = await this.db.findWorkspacesForGarbageCollection(14, 10); expect(dbResult.length).to.eq(0); } @test(timeout(10000)) public async testFindAllWorkspaces_contextUrl() { - await Promise.all([ - this.db.store(this.ws) - ]); + await Promise.all([this.db.store(this.ws)]); const dbResult = await this.db.findAllWorkspaces(0, 10, "contextURL", "DESC", undefined, this.ws.contextURL); expect(dbResult.total).to.eq(1); } @@ -323,13 +306,15 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; this.db.store(this.ws2), this.db.storeInstance(this.ws2i1), ]); - const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "workspaceId", "DESC", { workspaceId: this.ws2.id }); + const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "workspaceId", "DESC", { + workspaceId: this.ws2.id, + }); // It should only find one workspace instance expect(dbResult.total).to.eq(1); // It should find the workspace with the queried id - const workspaceAndInstance = dbResult.rows[0] - expect(workspaceAndInstance.workspaceId).to.eq(this.ws2.id) + const workspaceAndInstance = dbResult.rows[0]; + expect(workspaceAndInstance.workspaceId).to.eq(this.ws2.id); } @test(timeout(10000)) @@ -340,13 +325,15 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; this.db.store(this.ws2), this.db.storeInstance(this.ws2i1), ]); - const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "workspaceId", "DESC", { instanceIdOrWorkspaceId: this.ws2.id }); + const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "workspaceId", "DESC", { + instanceIdOrWorkspaceId: this.ws2.id, + }); // It should only find one workspace instance expect(dbResult.total).to.eq(1); // It should find the workspace with the queried id - const workspaceAndInstance = dbResult.rows[0] - expect(workspaceAndInstance.workspaceId).to.eq(this.ws2.id) + const workspaceAndInstance = dbResult.rows[0]; + expect(workspaceAndInstance.workspaceId).to.eq(this.ws2.id); } @test(timeout(10000)) @@ -358,17 +345,19 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; this.db.store(this.ws2), this.db.storeInstance(this.ws2i1), ]); - const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "instanceId", "DESC", { instanceId: this.wsi1.id }); + const dbResult = await this.db.findAllWorkspaceAndInstances(0, 10, "instanceId", "DESC", { + instanceId: this.wsi1.id, + }); // It should only find one workspace instance expect(dbResult.total).to.eq(1); // It should find the workspace with the queried id - const workspaceAndInstance = dbResult.rows[0] - expect(workspaceAndInstance.workspaceId).to.eq(this.ws.id) + const workspaceAndInstance = dbResult.rows[0]; + expect(workspaceAndInstance.workspaceId).to.eq(this.ws.id); // It should select the workspace instance that was queried, not the most recent one - expect(workspaceAndInstance.instanceId).to.eq(this.wsi1.id) + expect(workspaceAndInstance.instanceId).to.eq(this.wsi1.id); } @test(timeout(10000)) @@ -384,7 +373,7 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; userId: this.userId, includeHeadless: false, projectId: [this.projectAID], - includeWithoutProject: false + includeWithoutProject: false, }); // It should only find one workspace instance @@ -406,7 +395,7 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; userId: this.userId, includeHeadless: false, projectId: [this.projectBID], - includeWithoutProject: false + includeWithoutProject: false, }); // It should only find one workspace instance @@ -428,7 +417,7 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; userId: this.userId, includeHeadless: false, projectId: [this.projectAID, this.projectBID], - includeWithoutProject: false + includeWithoutProject: false, }); expect(dbResult.length).to.eq(2); @@ -450,7 +439,7 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; userId: this.userId, includeHeadless: false, projectId: [], - includeWithoutProject: false + includeWithoutProject: false, }); expect(dbResult.length).to.eq(0); @@ -474,7 +463,7 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; userId: this.userId, includeHeadless: false, projectId: [], - includeWithoutProject: true + includeWithoutProject: true, }); expect(dbResult.length).to.eq(1); @@ -497,7 +486,7 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; userId: this.userId, includeHeadless: false, projectId: [this.projectBID], - includeWithoutProject: true + includeWithoutProject: true, }); expect(dbResult.length).to.eq(2); @@ -514,42 +503,42 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; await Promise.all([ // Created now, and queued this.storePrebuiltWorkspace({ - id: 'prebuild123', - buildWorkspaceId: 'apples', + id: "prebuild123", + buildWorkspaceId: "apples", creationTime: now.toISOString(), cloneURL: cloneURL, - commit: '', - state: 'queued' + commit: "", + state: "queued", }), // now and aborted this.storePrebuiltWorkspace({ - id: 'prebuild456', - buildWorkspaceId: 'bananas', + id: "prebuild456", + buildWorkspaceId: "bananas", creationTime: now.toISOString(), cloneURL: cloneURL, - commit: '', - state: 'aborted' + commit: "", + state: "aborted", }), // completed over a minute ago this.storePrebuiltWorkspace({ - id: 'prebuild789', - buildWorkspaceId: 'oranges', + id: "prebuild789", + buildWorkspaceId: "oranges", creationTime: secondsBefore(now.toISOString(), 62), cloneURL: cloneURL, - commit: '', - state: 'available' + commit: "", + state: "available", }), ]); const minuteAgo = secondsBefore(now.toISOString(), 60); const unabortedCount = await this.db.countUnabortedPrebuildsSince(cloneURL, new Date(minuteAgo)); - expect(unabortedCount).to.eq(1) + expect(unabortedCount).to.eq(1); } private async storePrebuiltWorkspace(pws: PrebuiltWorkspace) { // store the creationTime directly, before it is modified by the store function in the ORM layer - const creationTime = pws.creationTime - await this.db.storePrebuiltWorkspace(pws) + const creationTime = pws.creationTime; + await this.db.storePrebuiltWorkspace(pws); const conn = await this.typeorm.getConnection(); const repo = conn.getRepository(DBPrebuiltWorkspace); @@ -558,8 +547,11 @@ import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; // MySQL requires the time format to be 2022-03-07 15:44:01.746141 // Looks almost like an ISO time string, hack it a bit. const mysqlTimeFormat = creationTime.replace("T", " ").replace("Z", ""); - await repo.query("UPDATE d_b_prebuilt_workspace SET creationTime = ? WHERE id = ?", [mysqlTimeFormat, pws.id]); + await repo.query("UPDATE d_b_prebuilt_workspace SET creationTime = ? WHERE id = ?", [ + mysqlTimeFormat, + pws.id, + ]); } } } -module.exports = new WorkspaceDBSpec() +module.exports = new WorkspaceDBSpec(); diff --git a/components/gitpod-db/src/workspace-db.ts b/components/gitpod-db/src/workspace-db.ts index 1b38d542a9f12b..c68876435163bd 100644 --- a/components/gitpod-db/src/workspace-db.ts +++ b/components/gitpod-db/src/workspace-db.ts @@ -4,27 +4,43 @@ * See License-AGPL.txt in the project root for license information. */ -import { DeepPartial } from 'typeorm'; - -import { Workspace, WorkspaceInfo, WorkspaceInstance, WorkspaceInstanceUser, WhitelistedRepository, Snapshot, LayoutData, PrebuiltWorkspace, PrebuiltWorkspaceUpdatable, RunningWorkspaceInfo, WorkspaceAndInstance, WorkspaceType, PrebuildInfo, AdminGetWorkspacesQuery, SnapshotState } from '@gitpod/gitpod-protocol'; +import { DeepPartial } from "typeorm"; + +import { + Workspace, + WorkspaceInfo, + WorkspaceInstance, + WorkspaceInstanceUser, + WhitelistedRepository, + Snapshot, + LayoutData, + PrebuiltWorkspace, + PrebuiltWorkspaceUpdatable, + RunningWorkspaceInfo, + WorkspaceAndInstance, + WorkspaceType, + PrebuildInfo, + AdminGetWorkspacesQuery, + SnapshotState, +} from "@gitpod/gitpod-protocol"; export type MaybeWorkspace = Workspace | undefined; export type MaybeWorkspaceInstance = WorkspaceInstance | undefined; export interface FindWorkspacesOptions { - userId: string - projectId?: string | string[] + userId: string; + projectId?: string | string[]; includeWithoutProject?: boolean; - limit?: number - searchString?: string - includeHeadless?: boolean - pinnedOnly?: boolean + limit?: number; + searchString?: string; + includeHeadless?: boolean; + pinnedOnly?: boolean; } export interface PrebuiltUpdatableAndWorkspace extends PrebuiltWorkspaceUpdatable { - prebuild: PrebuiltWorkspace - workspace: Workspace - instance: WorkspaceInstance + prebuild: PrebuiltWorkspace; + workspace: Workspace; + instance: WorkspaceInstance; } export type WorkspaceAuthData = Pick; @@ -49,9 +65,8 @@ export interface PrebuildWithWorkspace { export type WorkspaceAndOwner = Pick; export type WorkspaceOwnerAndSoftDeleted = Pick; -export const WorkspaceDB = Symbol('WorkspaceDB'); +export const WorkspaceDB = Symbol("WorkspaceDB"); export interface WorkspaceDB { - connect(maxTries: number, timeout: number): Promise; transaction(code: (db: WorkspaceDB) => Promise): Promise; @@ -67,7 +82,7 @@ export interface WorkspaceDB { // Partial update: unconditional, single field updates. Enclose in a transaction if necessary updateLastHeartbeat(instanceId: string, userId: string, newHeartbeat: Date, wasClosed?: boolean): Promise; - getLastOwnerHeartbeatFor(instance: WorkspaceInstance): Promise<{ lastSeen: Date, wasClosed?: boolean } | undefined>; + getLastOwnerHeartbeatFor(instance: WorkspaceInstance): Promise<{ lastSeen: Date; wasClosed?: boolean } | undefined>; getWorkspaceUsers(workspaceId: string, minLastSeen: number): Promise; updateInstancePartial(instanceId: string, partial: DeepPartial): Promise; @@ -76,32 +91,73 @@ export interface WorkspaceDB { findWorkspacesByUser(userId: string): Promise; findCurrentInstance(workspaceId: string): Promise; findRunningInstance(workspaceId: string): Promise; - findSessionsInPeriod(userId: string, periodStart: string, periodEnd: string): Promise; + findSessionsInPeriod( + userId: string, + periodStart: string, + periodEnd: string, + ): Promise; findWorkspacesForGarbageCollection(minAgeInDays: number, limit: number): Promise; - findWorkspacesForContentDeletion(minSoftDeletedTimeInDays: number, limit: number): Promise; + findWorkspacesForContentDeletion( + minSoftDeletedTimeInDays: number, + limit: number, + ): Promise; findPrebuiltWorkspacesForGC(daysUnused: number, limit: number): Promise; - findAllWorkspaces(offset: number, limit: number, orderBy: keyof Workspace, orderDir: "ASC" | "DESC", ownerId?: string, searchTerm?: string, minCreationTime?: Date, maxCreationDateTime?: Date, type?: WorkspaceType): Promise<{ total: number, rows: Workspace[] }>; - findAllWorkspaceAndInstances(offset: number, limit: number, orderBy: keyof WorkspaceAndInstance, orderDir: "ASC" | "DESC", query?: AdminGetWorkspacesQuery): Promise<{ total: number, rows: WorkspaceAndInstance[] }>; + findAllWorkspaces( + offset: number, + limit: number, + orderBy: keyof Workspace, + orderDir: "ASC" | "DESC", + ownerId?: string, + searchTerm?: string, + minCreationTime?: Date, + maxCreationDateTime?: Date, + type?: WorkspaceType, + ): Promise<{ total: number; rows: Workspace[] }>; + findAllWorkspaceAndInstances( + offset: number, + limit: number, + orderBy: keyof WorkspaceAndInstance, + orderDir: "ASC" | "DESC", + query?: AdminGetWorkspacesQuery, + ): Promise<{ total: number; rows: WorkspaceAndInstance[] }>; findWorkspaceAndInstance(id: string): Promise; findInstancesByPhaseAndRegion(phase: string, region: string): Promise; getWorkspaceCount(type?: String): Promise; - getInstanceCount(type?: string): Promise - - findAllWorkspaceInstances(offset: number, limit: number, orderBy: keyof WorkspaceInstance, orderDir: "ASC" | "DESC", ownerId?: string, minCreationTime?: Date, maxCreationTime?: Date, onlyRunning?: boolean, type?: WorkspaceType): Promise<{ total: number, rows: WorkspaceInstance[] }>; + getInstanceCount(type?: string): Promise; + + findAllWorkspaceInstances( + offset: number, + limit: number, + orderBy: keyof WorkspaceInstance, + orderDir: "ASC" | "DESC", + ownerId?: string, + minCreationTime?: Date, + maxCreationTime?: Date, + onlyRunning?: boolean, + type?: WorkspaceType, + ): Promise<{ total: number; rows: WorkspaceInstance[] }>; findRegularRunningInstances(userId?: string): Promise; - findRunningInstancesWithWorkspaces(installation?: string, userId?: string, includeStopping?: boolean): Promise; + findRunningInstancesWithWorkspaces( + installation?: string, + userId?: string, + includeStopping?: boolean, + ): Promise; isWhitelisted(repositoryUrl: string): Promise; getFeaturedRepositories(): Promise[]>; findSnapshotById(snapshotId: string): Promise; - findSnapshotsWithState(state: SnapshotState, offset: number, limit: number): Promise<{ snapshots: Snapshot[], total: number }>; + findSnapshotsWithState( + state: SnapshotState, + offset: number, + limit: number, + ): Promise<{ snapshots: Snapshot[]; total: number }>; findSnapshotsByWorkspaceId(workspaceId: string): Promise; storeSnapshot(snapshot: Snapshot): Promise; deleteSnapshot(snapshotId: string): Promise; - updateSnapshot(snapshot: DeepPartial & Pick): Promise; + updateSnapshot(snapshot: DeepPartial & Pick): Promise; storePrebuiltWorkspace(pws: PrebuiltWorkspace): Promise; findPrebuiltWorkspaceByCommit(cloneURL: string, commit: string): Promise; diff --git a/components/gitpod-protocol/src/accounting-protocol.ts b/components/gitpod-protocol/src/accounting-protocol.ts index f00e5ab65e90a3..68f7e89cb9d600 100644 --- a/components/gitpod-protocol/src/accounting-protocol.ts +++ b/components/gitpod-protocol/src/accounting-protocol.ts @@ -4,9 +4,9 @@ * See License-AGPL.txt in the project root for license information. */ - import { v4 as uuidv4 } from 'uuid'; -import { User } from './protocol'; -import { oneMonthLater } from './util/timeutil'; +import { v4 as uuidv4 } from "uuid"; +import { User } from "./protocol"; +import { oneMonthLater } from "./util/timeutil"; /* * Subscription and acocunting data @@ -33,7 +33,7 @@ export interface AccountEntry { /** * credit: end of validity */ - expiryDate?: string; // exclusive + expiryDate?: string; // exclusive kind: AccountEntryKind; @@ -46,26 +46,26 @@ export interface AccountEntry { description?: object; } export namespace AccountEntry { - export function create(entry: Omit): T { + export function create(entry: Omit): T { const result = entry as T; result.uid = uuidv4(); return result; - }; + } } -export type DebitAccountEntryKind = 'session' | 'expiry' | 'loss'; -export type AccountEntryKind = 'credit' | DebitAccountEntryKind | 'carry' | 'open'; +export type DebitAccountEntryKind = "session" | "expiry" | "loss"; +export type AccountEntryKind = "credit" | DebitAccountEntryKind | "carry" | "open"; export interface Credit extends AccountEntry { - kind: 'credit'; + kind: "credit"; expiryDate: string; } export type Debit = LossDebit | ExpiryDebit | SessionDebit; export interface LossDebit extends AccountEntry { - kind: 'loss'; + kind: "loss"; } export interface ExpiryDebit extends AccountEntry { - kind: 'expiry'; + kind: "expiry"; creditId: undefined; } export interface SessionDebit extends AccountEntry { @@ -80,9 +80,7 @@ export interface CreditDescription { } export namespace CreditDescription { export function is(obj: any): obj is CreditDescription { - return !!obj - && obj.hasOwnProperty('subscriptionId') - && obj.hasOwnProperty('planId'); + return !!obj && obj.hasOwnProperty("subscriptionId") && obj.hasOwnProperty("planId"); } } export interface SessionDescription { @@ -93,11 +91,13 @@ export interface SessionDescription { } export namespace SessionDescription { export function is(obj: any): obj is SessionDescription { - return !!obj - && obj.hasOwnProperty('contextTitle') - && obj.hasOwnProperty('contextUrl') - && obj.hasOwnProperty('workspaceId') - && obj.hasOwnProperty('workspaceInstanceId') + return ( + !!obj && + obj.hasOwnProperty("contextTitle") && + obj.hasOwnProperty("contextUrl") && + obj.hasOwnProperty("workspaceId") && + obj.hasOwnProperty("workspaceInstanceId") + ); } } @@ -111,11 +111,11 @@ export namespace SessionDescription { export interface Subscription { uid: string; userId: string; - startDate: string; // inclusive + startDate: string; // inclusive /** When the subscription will end (must be >= cancellationDate!) */ - endDate?: string; // exclusive + endDate?: string; // exclusive /** When the subscription was cancelled */ - cancellationDate?: string; // exclusive + cancellationDate?: string; // exclusive /** Number of granted hours */ amount: number; /** Number of granted hours for the first month: If this is set, use this value for the first month */ @@ -145,8 +145,7 @@ export interface UserPaidSubscription extends Subscription { } export namespace UserPaidSubscription { export function is(data: any): data is UserPaidSubscription { - return !!data - && data.hasOwnProperty('paymentReference'); + return !!data && data.hasOwnProperty("paymentReference"); } } @@ -155,41 +154,44 @@ export interface AssignedTeamSubscription extends Subscription { } export namespace AssignedTeamSubscription { export function is(data: any): data is AssignedTeamSubscription { - return !!data - && data.hasOwnProperty('teamSubscriptionSlotId'); + return !!data && data.hasOwnProperty("teamSubscriptionSlotId"); } } export namespace Subscription { - export function create(newSubscription: Omit) { + export function create(newSubscription: Omit) { const subscription = newSubscription as Subscription; subscription.uid = uuidv4(); return subscription; - }; + } export function cancelSubscription(s: Subscription, cancellationDate: string, endDate?: string) { s.endDate = endDate || cancellationDate; s.cancellationDate = cancellationDate; - }; + } export function isSame(s1: Subscription | undefined, s2: Subscription | undefined): boolean { - return !!s1 && !!s2 - && s1.userId === s2.userId - && s1.planId === s2.planId - && s1.startDate === s2.startDate - && s1.endDate === s2.endDate - && s1.amount === s2.amount - && s1.cancellationDate === s2.cancellationDate - && s1.deleted === s2.deleted - && ((s1.paymentData === undefined && s2.paymentData === undefined) - || (!!s1.paymentData && !!s2.paymentData - && s1.paymentData.downgradeDate === s2.paymentData.downgradeDate - && s1.paymentData.newPlan === s2.paymentData.newPlan)); - }; + return ( + !!s1 && + !!s2 && + s1.userId === s2.userId && + s1.planId === s2.planId && + s1.startDate === s2.startDate && + s1.endDate === s2.endDate && + s1.amount === s2.amount && + s1.cancellationDate === s2.cancellationDate && + s1.deleted === s2.deleted && + ((s1.paymentData === undefined && s2.paymentData === undefined) || + (!!s1.paymentData && + !!s2.paymentData && + s1.paymentData.downgradeDate === s2.paymentData.downgradeDate && + s1.paymentData.newPlan === s2.paymentData.newPlan)) + ); + } export function isActive(s: Subscription, date: string): boolean { return s.startDate <= date && (s.endDate === undefined || date < s.endDate); - }; + } export function isDowngraded(s: Subscription) { return s.paymentData && s.paymentData.downgradeDate; - }; + } export function calculateCurrentPeriod(startDate: string, now: Date) { let nextStartDate = startDate; do { @@ -197,19 +199,19 @@ export namespace Subscription { nextStartDate = oneMonthLater(startDate, new Date(startDate).getDate()); } while (nextStartDate < now.toISOString()); return { startDate, endDate: nextStartDate }; - }; + } } export type MaybeSubscription = Subscription | undefined; export interface Period { - startDate: string; // inclusive - endDate: string; // exclusive + startDate: string; // inclusive + endDate: string; // exclusive } export type MaybePeriod = Period | undefined; -export type AccountEntryFixedPeriod = Omit & { expiryDate: string }; +export type AccountEntryFixedPeriod = Omit & { expiryDate: string }; export interface AccountStatement extends Period { userId: string; /** @@ -221,9 +223,9 @@ export interface AccountStatement extends Period { /** Remaining valid hours (accumulated from credits) */ remainingHours: RemainingHours; } -export type RemainingHours = number | 'unlimited'; +export type RemainingHours = number | "unlimited"; export interface CreditAlert { - userId: string, - remainingUsageHours: number + userId: string; + remainingUsageHours: number; } diff --git a/components/gitpod-protocol/src/admin-protocol.ts b/components/gitpod-protocol/src/admin-protocol.ts index 5c23f40691ea29..25153c995a7eaa 100644 --- a/components/gitpod-protocol/src/admin-protocol.ts +++ b/components/gitpod-protocol/src/admin-protocol.ts @@ -6,7 +6,7 @@ import { User, Workspace, NamedWorkspaceFeatureFlag } from "./protocol"; import { FindPrebuildsParams } from "./gitpod-service"; -import { Project, Team, PrebuildWithStatus, TeamMemberInfo, TeamMemberRole } from "./teams-projects-protocol" +import { Project, Team, PrebuildWithStatus, TeamMemberInfo, TeamMemberRole } from "./teams-projects-protocol"; import { WorkspaceInstance, WorkspaceInstancePhase } from "./workspace-instance"; import { RoleOrPermission } from "./permission"; import { AccountStatement } from "./accounting-protocol"; @@ -42,45 +42,47 @@ export interface AdminServer { adminAddStudentEmailDomain(userId: string, domain: string): Promise; adminGrantExtraHours(userId: string, extraHours: number): Promise; - adminGetSettings(): Promise - adminUpdateSettings(settings: InstallationAdminSettings): Promise + adminGetSettings(): Promise; + adminUpdateSettings(settings: InstallationAdminSettings): Promise; } export interface AdminGetListRequest { - offset: number - limit: number - orderBy: keyof T - orderDir: "asc" | "desc" + offset: number; + limit: number; + orderBy: keyof T; + orderDir: "asc" | "desc"; searchTerm?: string; } export interface AdminGetListResult { - total: number - rows: T[] + total: number; + rows: T[]; } export interface AdminBlockUserRequest { - id: string - blocked: boolean + id: string; + blocked: boolean; } export interface AdminModifyRoleOrPermissionRequest { id: string; rpp: { - r: RoleOrPermission - add: boolean - }[] + r: RoleOrPermission; + add: boolean; + }[]; } export interface AdminModifyPermanentWorkspaceFeatureFlagRequest { id: string; changes: { - featureFlag: NamedWorkspaceFeatureFlag - add: boolean - }[] + featureFlag: NamedWorkspaceFeatureFlag; + add: boolean; + }[]; } -export interface WorkspaceAndInstance extends Omit, Omit { +export interface WorkspaceAndInstance + extends Omit, + Omit { workspaceId: string; workspaceCreationTime: string; instanceId: string; @@ -93,7 +95,7 @@ export namespace WorkspaceAndInstance { return { id: wai.workspaceId, creationTime: wai.workspaceCreationTime, - ...wai + ...wai, }; } @@ -104,7 +106,7 @@ export namespace WorkspaceAndInstance { return { id: wai.instanceId, creationTime: wai.instanceCreationTime, - ...wai + ...wai, }; } } diff --git a/components/gitpod-protocol/src/analytics.ts b/components/gitpod-protocol/src/analytics.ts index ce5eafb3beeab3..4483f2bade53ab 100644 --- a/components/gitpod-protocol/src/analytics.ts +++ b/components/gitpod-protocol/src/analytics.ts @@ -4,7 +4,6 @@ * See License-AGPL.txt in the project root for license information. */ - export const IAnalyticsWriter = Symbol("IAnalyticsWriter"); type Identity = @@ -15,24 +14,27 @@ interface Message { messageId?: string; } -export type IdentifyMessage = Message & Identity & { - traits?: any; - timestamp?: Date; - context?: any; -}; - -export type TrackMessage = Message & Identity & { - event: string; - properties?: any; - timestamp?: Date; - context?: any; -}; - -export type PageMessage = Message & Identity & { - properties?: any; - timestamp?: Date; - context?: any; -}; +export type IdentifyMessage = Message & + Identity & { + traits?: any; + timestamp?: Date; + context?: any; + }; + +export type TrackMessage = Message & + Identity & { + event: string; + properties?: any; + timestamp?: Date; + context?: any; + }; + +export type PageMessage = Message & + Identity & { + properties?: any; + timestamp?: Date; + context?: any; + }; export type RemoteTrackMessage = Omit; export type RemotePageMessage = Omit & { @@ -42,11 +44,9 @@ export type RemotePageMessage = Omit & { export type RemoteIdentifyMessage = Omit; export interface IAnalyticsWriter { - identify(msg: IdentifyMessage): void; track(msg: TrackMessage): void; page(msg: PageMessage): void; - } diff --git a/components/gitpod-protocol/src/auth.ts b/components/gitpod-protocol/src/auth.ts index 6a48a892a2de26..f9d523b0ba150e 100644 --- a/components/gitpod-protocol/src/auth.ts +++ b/components/gitpod-protocol/src/auth.ts @@ -11,14 +11,14 @@ export interface SelectAccountPayload { authHost: string; authName: string; authProviderType: string; - }, + }; otherUser: { name: string; avatarUrl: string; authHost: string; authName: string; authProviderType: string; - } + }; } export namespace SelectAccountPayload { export function is(data: any): data is SelectAccountPayload { diff --git a/components/gitpod-protocol/src/context-url.spec.ts b/components/gitpod-protocol/src/context-url.spec.ts index d2a7acbca875b2..7f31e59060a509 100644 --- a/components/gitpod-protocol/src/context-url.spec.ts +++ b/components/gitpod-protocol/src/context-url.spec.ts @@ -4,43 +4,57 @@ * See License-AGPL.txt in the project root for license information. */ -import * as chai from 'chai'; -import { suite, test } from 'mocha-typescript'; -import { Workspace } from '.'; -import { ContextURL } from './context-url'; +import * as chai from "chai"; +import { suite, test } from "mocha-typescript"; +import { Workspace } from "."; +import { ContextURL } from "./context-url"; const expect = chai.expect; type WsContextUrl = Pick; @suite export class ContextUrlTest { - @test public parseContextUrl_withEnvVar() { - const actual = ContextURL.getNormalizedURL({ contextURL: "passedin=test%20value/https://github.com/gitpod-io/gitpod-test-repo", context: {} } as WsContextUrl); + const actual = ContextURL.getNormalizedURL({ + contextURL: "passedin=test%20value/https://github.com/gitpod-io/gitpod-test-repo", + context: {}, + } as WsContextUrl); expect(actual?.host).to.equal("github.com"); expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo"); } @test public parseContextUrl_withEnvVar_withoutSchema() { - const actual = ContextURL.getNormalizedURL({ contextURL: "passedin=test%20value/github.com/gitpod-io/gitpod-test-repo", context: {} } as WsContextUrl); + const actual = ContextURL.getNormalizedURL({ + contextURL: "passedin=test%20value/github.com/gitpod-io/gitpod-test-repo", + context: {}, + } as WsContextUrl); expect(actual?.host).to.equal("github.com"); expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo"); } @test public parseContextUrl_withEnvVar_sshUrl() { - const actual = ContextURL.getNormalizedURL({ contextURL: "passedin=test%20value/git@github.com:gitpod-io/gitpod-test-repo.git", context: {} } as WsContextUrl); + const actual = ContextURL.getNormalizedURL({ + contextURL: "passedin=test%20value/git@github.com:gitpod-io/gitpod-test-repo.git", + context: {}, + } as WsContextUrl); expect(actual?.host).to.equal("github.com"); expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo.git"); } @test public parseContextUrl_withPrebuild() { - const actual = ContextURL.getNormalizedURL({ contextURL: "prebuild/https://github.com/gitpod-io/gitpod-test-repo", context: {} } as WsContextUrl); + const actual = ContextURL.getNormalizedURL({ + contextURL: "prebuild/https://github.com/gitpod-io/gitpod-test-repo", + context: {}, + } as WsContextUrl); expect(actual?.host).to.equal("github.com"); expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo"); } @test public parseContextUrl_withPrebuild_withoutSchema() { - const actual = ContextURL.getNormalizedURL({ contextURL: "prebuild/github.com/gitpod-io/gitpod-test-repo", context: {} } as WsContextUrl); + const actual = ContextURL.getNormalizedURL({ + contextURL: "prebuild/github.com/gitpod-io/gitpod-test-repo", + context: {}, + } as WsContextUrl); expect(actual?.host).to.equal("github.com"); expect(actual?.pathname).to.equal("/gitpod-io/gitpod-test-repo"); } @@ -50,4 +64,4 @@ export class ContextUrlTest { expect(actual).to.be.undefined; } } -module.exports = new ContextUrlTest() \ No newline at end of file +module.exports = new ContextUrlTest(); diff --git a/components/gitpod-protocol/src/context-url.ts b/components/gitpod-protocol/src/context-url.ts index a7dbd17679f54d..3fe1b4e8b97209 100644 --- a/components/gitpod-protocol/src/context-url.ts +++ b/components/gitpod-protocol/src/context-url.ts @@ -15,93 +15,95 @@ import { Workspace } from "."; * TODO(gpl) See if we can get this into `server` code to remove the burden from clients */ export namespace ContextURL { - export const INCREMENTAL_PREBUILD_PREFIX = "incremental-prebuild"; - export const PREBUILD_PREFIX = "prebuild"; - export const IMAGEBUILD_PREFIX = "imagebuild"; - export const SNAPSHOT_PREFIX = "snapshot"; - export const REFERRER_PREFIX = 'referrer:'; + export const INCREMENTAL_PREBUILD_PREFIX = "incremental-prebuild"; + export const PREBUILD_PREFIX = "prebuild"; + export const IMAGEBUILD_PREFIX = "imagebuild"; + export const SNAPSHOT_PREFIX = "snapshot"; + export const REFERRER_PREFIX = "referrer:"; - /** - * This function will (try to) return the HTTP(S) URL of the context the user originally created this workspace on. - * Especially it will not contain any modifiers or be of different scheme than HTTP(S). - * - * Use this function if you need to provided a _working_ URL to the original context. - * @param ws - * @returns - */ - export function getNormalizedURL(ws: Pick | undefined): URL | undefined { - const normalized = normalize(ws); - if (!normalized) { - return undefined; - } + /** + * This function will (try to) return the HTTP(S) URL of the context the user originally created this workspace on. + * Especially it will not contain any modifiers or be of different scheme than HTTP(S). + * + * Use this function if you need to provided a _working_ URL to the original context. + * @param ws + * @returns + */ + export function getNormalizedURL(ws: Pick | undefined): URL | undefined { + const normalized = normalize(ws); + if (!normalized) { + return undefined; + } - try { - return new URL(normalized); - } catch (err) { - console.error(`unable to parse URL from normalized contextURL: '${normalized}'`, err); + try { + return new URL(normalized); + } catch (err) { + console.error(`unable to parse URL from normalized contextURL: '${normalized}'`, err); + } + return undefined; } - return undefined; - } - function normalize(ws: Pick | undefined): string | undefined { - if (!ws) { - return undefined; - } - if (ws.context.normalizedContextURL) { - return ws.context.normalizedContextURL; - } + function normalize(ws: Pick | undefined): string | undefined { + if (!ws) { + return undefined; + } + if (ws.context.normalizedContextURL) { + return ws.context.normalizedContextURL; + } - // fallback: we do not yet set normalizedContextURL on all workspaces, yet, let alone older existing workspaces - let fallback: string | undefined = undefined; - try { - fallback = removePrefixes(ws.contextURL); - } catch (err) { - console.error(`unable to remove prefixes from contextURL: '${ws.contextURL}'`, err); + // fallback: we do not yet set normalizedContextURL on all workspaces, yet, let alone older existing workspaces + let fallback: string | undefined = undefined; + try { + fallback = removePrefixes(ws.contextURL); + } catch (err) { + console.error(`unable to remove prefixes from contextURL: '${ws.contextURL}'`, err); + } + return fallback; } - return fallback; - } - /** - * The field "contextUrl" might contain prefixes like: - * - envvar1=value1/... - * - prebuild/... - * This is the analogon to the (Prefix)ContextParser structure in "server". - */ - function removePrefixes(contextUrl: string | undefined): string | undefined { - if (contextUrl === undefined) { - return undefined; - } + /** + * The field "contextUrl" might contain prefixes like: + * - envvar1=value1/... + * - prebuild/... + * This is the analogon to the (Prefix)ContextParser structure in "server". + */ + function removePrefixes(contextUrl: string | undefined): string | undefined { + if (contextUrl === undefined) { + return undefined; + } - const segments = contextUrl.split("/"); - if (segments.length === 1) { - return segments[0]; // this might be something, we just try - } + const segments = contextUrl.split("/"); + if (segments.length === 1) { + return segments[0]; // this might be something, we just try + } - const segmentsToURL = (offset: number): string => { - let rest = segments.slice(offset).join("/"); - if (/^git@[^:\/]+:/.test(rest)) { - rest = rest.replace(/^git@([^:\/]+):/, 'https://$1/'); - } - if (!rest.startsWith("http")) { - rest = 'https://' + rest; - } - return rest; - }; + const segmentsToURL = (offset: number): string => { + let rest = segments.slice(offset).join("/"); + if (/^git@[^:\/]+:/.test(rest)) { + rest = rest.replace(/^git@([^:\/]+):/, "https://$1/"); + } + if (!rest.startsWith("http")) { + rest = "https://" + rest; + } + return rest; + }; - const firstSegment = segments[0]; - if (firstSegment === PREBUILD_PREFIX || - firstSegment === INCREMENTAL_PREBUILD_PREFIX || - firstSegment === IMAGEBUILD_PREFIX || - firstSegment === SNAPSHOT_PREFIX || - firstSegment.startsWith(REFERRER_PREFIX)) { - return segmentsToURL(1); - } + const firstSegment = segments[0]; + if ( + firstSegment === PREBUILD_PREFIX || + firstSegment === INCREMENTAL_PREBUILD_PREFIX || + firstSegment === IMAGEBUILD_PREFIX || + firstSegment === SNAPSHOT_PREFIX || + firstSegment.startsWith(REFERRER_PREFIX) + ) { + return segmentsToURL(1); + } - // check for env vars - if (firstSegment.indexOf("=") !== -1) { - return segmentsToURL(1); - } + // check for env vars + if (firstSegment.indexOf("=") !== -1) { + return segmentsToURL(1); + } - return segmentsToURL(0); - } -} \ No newline at end of file + return segmentsToURL(0); + } +} diff --git a/components/gitpod-protocol/src/email-protocol.ts b/components/gitpod-protocol/src/email-protocol.ts index dc8cb0e86b3ccd..cbc20f058b3e9f 100644 --- a/components/gitpod-protocol/src/email-protocol.ts +++ b/components/gitpod-protocol/src/email-protocol.ts @@ -4,10 +4,9 @@ * See License-AGPL.txt in the project root for license information. */ -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; -export type EMailState = "scheduledInternal" - | "scheduledSendgrid"; +export type EMailState = "scheduledInternal" | "scheduledSendgrid"; export interface EMailStatus { /** The time the email entry was inserted into the DB */ @@ -58,9 +57,9 @@ export type EMail = { } & EMailStatus; export namespace EMail { - export const create = (ts: Omit): EMail => { + export const create = (ts: Omit): EMail => { const withId = ts as EMail; withId.uid = uuidv4(); return withId; - } -} \ No newline at end of file + }; +} diff --git a/components/gitpod-protocol/src/encryption/container-module.ts b/components/gitpod-protocol/src/encryption/container-module.ts index 071e6afe71e48f..7475ca9dfbfa40 100644 --- a/components/gitpod-protocol/src/encryption/container-module.ts +++ b/components/gitpod-protocol/src/encryption/container-module.ts @@ -19,10 +19,9 @@ import { EncryptionService, EncryptionServiceImpl } from "./encryption-service"; * }; * }).inSingletonScope(); */ -export const encryptionModule: interfaces.ContainerModuleCallBack = bind => { - +export const encryptionModule: interfaces.ContainerModuleCallBack = (bind) => { bind(KeyProvider).to(KeyProviderImpl).inSingletonScope(); bind(EncryptionEngine).to(EncryptionEngineImpl).inSingletonScope(); bind(EncryptionService).to(EncryptionServiceImpl).inSingletonScope(); -}; \ No newline at end of file +}; diff --git a/components/gitpod-protocol/src/encryption/encryption-engine.spec.ts b/components/gitpod-protocol/src/encryption/encryption-engine.spec.ts index 66ab51d59311a3..41a905911191b6 100644 --- a/components/gitpod-protocol/src/encryption/encryption-engine.spec.ts +++ b/components/gitpod-protocol/src/encryption/encryption-engine.spec.ts @@ -5,25 +5,26 @@ */ import { suite, test } from "mocha-typescript"; -import * as chai from 'chai'; -import * as path from 'path'; +import * as chai from "chai"; +import * as path from "path"; import * as fs from "fs"; import { EncryptionEngineImpl } from "./encryption-engine"; const expect = chai.expect; -@suite class TestEncryptionEngineImpl { +@suite +class TestEncryptionEngineImpl { // Created with openssl rand -rand /dev/urandom -out key -base64 32 - protected get testkey () { - const keyFilePath = path.resolve(__dirname, '../../test/fixtures/encryption/testkey'); + protected get testkey() { + const keyFilePath = path.resolve(__dirname, "../../test/fixtures/encryption/testkey"); const keyBuffer = fs.readFileSync(keyFilePath); return keyBuffer.toString().trim(); - }; + } @test basicSymmetry() { const plaintext = "12345678901234567890"; - const key = new Buffer(this.testkey, 'base64'); + const key = new Buffer(this.testkey, "base64"); const cut = new EncryptionEngineImpl(); const encryptedData = cut.encrypt(plaintext, key); @@ -33,4 +34,4 @@ const expect = chai.expect; expect(decryptedPlaintext).equals(plaintext); } } -export const t = new TestEncryptionEngineImpl(); \ No newline at end of file +export const t = new TestEncryptionEngineImpl(); diff --git a/components/gitpod-protocol/src/encryption/encryption-engine.ts b/components/gitpod-protocol/src/encryption/encryption-engine.ts index b7946ba8363594..7687ed2e986644 100644 --- a/components/gitpod-protocol/src/encryption/encryption-engine.ts +++ b/components/gitpod-protocol/src/encryption/encryption-engine.ts @@ -4,20 +4,20 @@ * See License-AGPL.txt in the project root for license information. */ -import * as crypto from 'crypto'; -import { injectable } from 'inversify'; +import * as crypto from "crypto"; +import { injectable } from "inversify"; export interface KeyParams { - iv: string + iv: string; } export interface EncryptedData { /** utf8 encoded string */ - data: string, - keyParams: KeyParams + data: string; + keyParams: KeyParams; } -export const EncryptionEngine = Symbol('EncryptionEngine'); +export const EncryptionEngine = Symbol("EncryptionEngine"); export interface EncryptionEngine { /** * @param data utf8 encoded string @@ -32,21 +32,21 @@ export interface EncryptionEngine { * - no salt, as we pass in a real key (no salting needed to turn a password into a key) * The implementation closely follows the exampes in https://nodejs.org/api/crypto.html. */ - @injectable() +@injectable() export class EncryptionEngineImpl { - readonly algorithm = 'aes-256-cbc'; - readonly enc = 'base64'; + readonly algorithm = "aes-256-cbc"; + readonly enc = "base64"; encrypt(data: string, key: Buffer): EncryptedData { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(this.algorithm, key, iv); - const encrypted = cipher.update(new Buffer(data, 'utf8')); + const encrypted = cipher.update(new Buffer(data, "utf8")); const finalEncrypted = Buffer.concat([encrypted, cipher.final()]); return { data: finalEncrypted.toString(this.enc), keyParams: { - iv: iv.toString(this.enc) - } + iv: iv.toString(this.enc), + }, }; } @@ -54,6 +54,6 @@ export class EncryptionEngineImpl { const decipher = crypto.createDecipheriv(this.algorithm, key, new Buffer(encryptedData.keyParams.iv, this.enc)); let decrypted = decipher.update(new Buffer(encryptedData.data, this.enc)); const finalDecrypted = Buffer.concat([decrypted, decipher.final()]); - return finalDecrypted.toString('utf8'); + return finalDecrypted.toString("utf8"); } -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/encryption/encryption-service.ts b/components/gitpod-protocol/src/encryption/encryption-service.ts index 7c2ea72e2a18d3..0e2622ffc31a8b 100644 --- a/components/gitpod-protocol/src/encryption/encryption-service.ts +++ b/components/gitpod-protocol/src/encryption/encryption-service.ts @@ -9,12 +9,11 @@ import { injectable, inject } from "inversify"; import { EncryptedData, EncryptionEngine } from "./encryption-engine"; import { KeyProvider, KeyMetadata } from "./key-provider"; - export interface Encrypted<_T> extends EncryptedData { - keyMetadata: KeyMetadata + keyMetadata: KeyMetadata; } -export const EncryptionService = Symbol('EncryptionService'); +export const EncryptionService = Symbol("EncryptionService"); export interface EncryptionService { encrypt(data: T): Encrypted; decrypt(encrypted: Encrypted): T; @@ -32,7 +31,7 @@ export class EncryptionServiceImpl implements EncryptionService { const encryptedData = this.engine.encrypt(dataStr, key.material); return { ...encryptedData, - keyMetadata: key.metadata + keyMetadata: key.metadata, }; } @@ -49,4 +48,4 @@ export class EncryptionServiceImpl implements EncryptionService { protected deserialize(data: string): T { return JSON.parse(data) as T; } -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/encryption/key-provider.ts b/components/gitpod-protocol/src/encryption/key-provider.ts index fb140faae3847d..b0a160c9bc28bd 100644 --- a/components/gitpod-protocol/src/encryption/key-provider.ts +++ b/components/gitpod-protocol/src/encryption/key-provider.ts @@ -7,16 +7,16 @@ import { injectable, inject } from "inversify"; export interface KeyMetadata { - name: string, - version: number + name: string; + version: number; } export interface Key { - metadata: KeyMetadata, - material: Buffer + metadata: KeyMetadata; + material: Buffer; } -export const KeyProvider = Symbol('KeyProvider'); +export const KeyProvider = Symbol("KeyProvider"); export interface KeyProvider { getPrimaryKey(): Key; getKeyFor(metadata: KeyMetadata): Key; @@ -24,24 +24,21 @@ export interface KeyProvider { export type KeyConfig = KeyMetadata & { /** base64 encoded */ - material: string, - primary?: boolean -} + material: string; + primary?: boolean; +}; -export const KeyProviderConfig = Symbol('KeyProviderConfig'); +export const KeyProviderConfig = Symbol("KeyProviderConfig"); export interface KeyProviderConfig { - keys: KeyConfig[] + keys: KeyConfig[]; } @injectable() export class KeyProviderImpl implements KeyProvider { - static loadKeyConfigFromJsonString(configStr: string): KeyConfig[] { const keys = (JSON.parse(configStr) || []) as KeyConfig[]; - if (!Array.isArray(keys) - || keys.length < 0 - || 1 !== keys.reduce((p, k) => k.primary ? p + 1 : p, 0)) { - throw new Error('Invalid key config!'); + if (!Array.isArray(keys) || keys.length < 0 || 1 !== keys.reduce((p, k) => (k.primary ? p + 1 : p), 0)) { + throw new Error("Invalid key config!"); } return keys; } @@ -49,13 +46,13 @@ export class KeyProviderImpl implements KeyProvider { constructor(@inject(KeyProviderConfig) protected readonly config: KeyProviderConfig) {} protected get keys() { - return this.config.keys + return this.config.keys; } getPrimaryKey(): Key { const primaryKey = this.keys.find((key) => !!key.primary); if (!primaryKey) { - throw new Error('No primary encryption key found!'); + throw new Error("No primary encryption key found!"); } return this.configToKey(primaryKey); } @@ -72,9 +69,9 @@ export class KeyProviderImpl implements KeyProvider { return { metadata: { name: config.name, - version: config.version + version: config.version, }, - material: new Buffer(config.material, 'base64') + material: new Buffer(config.material, "base64"), }; } } diff --git a/components/gitpod-protocol/src/env.ts b/components/gitpod-protocol/src/env.ts index 5277b83a2c8d78..68b108debfc31c 100644 --- a/components/gitpod-protocol/src/env.ts +++ b/components/gitpod-protocol/src/env.ts @@ -7,11 +7,11 @@ import { injectable } from "inversify"; const legacyStagenameTranslation: { [key: string]: KubeStage } = { - "production": "production", - "staging": "prodcopy", - "devstaging": "dev", - "dev": "dev" -} + production: "production", + staging: "prodcopy", + devstaging: "dev", + dev: "dev", +}; export function translateLegacyStagename(kubeStage: string): KubeStage { const stage = legacyStagenameTranslation[kubeStage]; @@ -24,13 +24,13 @@ export function translateLegacyStagename(kubeStage: string): KubeStage { @injectable() export abstract class AbstractComponentEnv { - readonly kubeStage: KubeStage = getEnvVarParsed('KUBE_STAGE', translateLegacyStagename); + readonly kubeStage: KubeStage = getEnvVarParsed("KUBE_STAGE", translateLegacyStagename); - readonly installationLongname: string = getEnvVar("GITPOD_INSTALLATION_LONGNAME") - readonly installationShortname: string = getEnvVar("GITPOD_INSTALLATION_SHORTNAME") + readonly installationLongname: string = getEnvVar("GITPOD_INSTALLATION_LONGNAME"); + readonly installationShortname: string = getEnvVar("GITPOD_INSTALLATION_SHORTNAME"); } -export type KubeStage = 'production' | 'prodcopy' | 'staging' | 'dev'; +export type KubeStage = "production" | "prodcopy" | "staging" | "dev"; export function getEnvVar(name: string, defaultValue?: string): string { const value = process.env[name] || defaultValue; diff --git a/components/gitpod-protocol/src/gitpod-file-parser.spec.ts b/components/gitpod-protocol/src/gitpod-file-parser.spec.ts index 7496330457ba87..a15c067b088fd6 100644 --- a/components/gitpod-protocol/src/gitpod-file-parser.spec.ts +++ b/components/gitpod-protocol/src/gitpod-file-parser.spec.ts @@ -4,19 +4,19 @@ * See License-AGPL.txt in the project root for license information. */ -import { suite, test } from "mocha-typescript" -import * as chai from "chai" +import { suite, test } from "mocha-typescript"; +import * as chai from "chai"; -import { WorkspaceConfig } from './protocol'; -import { GitpodFileParser } from './gitpod-file-parser'; +import { WorkspaceConfig } from "./protocol"; +import { GitpodFileParser } from "./gitpod-file-parser"; -const expect = chai.expect +const expect = chai.expect; const DEFAULT_IMAGE = "default-image"; const DEFAULT_CONFIG = { image: DEFAULT_IMAGE }; -@suite class TestGitpodFileParser { - +@suite +class TestGitpodFileParser { protected parser: GitpodFileParser; public before() { @@ -24,107 +24,102 @@ const DEFAULT_CONFIG = { image: DEFAULT_IMAGE }; } @test public testOnlyOnePort() { - const content = - `ports:\n` + - ` - port: 5555`; + const content = `ports:\n` + ` - port: 5555`; const result = this.parser.parse(content, {}, DEFAULT_CONFIG); expect(result.config).to.deep.equal({ - ports: [{ - port: 5555 - }], - image: DEFAULT_IMAGE + ports: [ + { + port: 5555, + }, + ], + image: DEFAULT_IMAGE, }); } @test public testPortRange() { - const content = - `ports:\n` + - ` - port: 5555\n` + - ` - port: 3000-3999`; // should be filtered out by default + const content = `ports:\n` + ` - port: 5555\n` + ` - port: 3000-3999`; // should be filtered out by default const result = this.parser.parse(content, {}, DEFAULT_CONFIG); expect(result.config).to.deep.equal({ - ports: [{ - port: 5555 - }], - image: DEFAULT_IMAGE + ports: [ + { + port: 5555, + }, + ], + image: DEFAULT_IMAGE, }); } @test public testPortRangeAccepted() { - const content = - `ports:\n` + - ` - port: 5555\n` + - ` - port: 3000-3999`; // should be included if explicitly supported + const content = `ports:\n` + ` - port: 5555\n` + ` - port: 3000-3999`; // should be included if explicitly supported const result = this.parser.parse(content, { acceptPortRanges: true }, DEFAULT_CONFIG); expect(result.config).to.deep.equal({ - ports: [{ - port: 5555 - }, { - port: '3000-3999' - }], - image: DEFAULT_IMAGE + ports: [ + { + port: 5555, + }, + { + port: "3000-3999", + }, + ], + image: DEFAULT_IMAGE, }); } @test public testSimpleTask() { - const content = - `tasks:\n` + - ` - command: yarn`; + const content = `tasks:\n` + ` - command: yarn`; const result = this.parser.parse(content, {}, DEFAULT_CONFIG); expect(result.config).to.deep.equal({ - tasks: [{ - command: "yarn" - }], - image: DEFAULT_IMAGE + tasks: [ + { + command: "yarn", + }, + ], + image: DEFAULT_IMAGE, }); } @test public testSimpleImage() { const imageName = "my-test-org/my-test-image:some-tag"; - const content = - `image: "${imageName}"\n`; + const content = `image: "${imageName}"\n`; const result = this.parser.parse(content); expect(result.config).to.deep.equal({ - image: imageName + image: imageName, }); } @test public testComplexImageWithoutContext() { - const dockerFileName = 'Dockerfile'; - const content = - `image:\n file: ${dockerFileName}\n`; + const dockerFileName = "Dockerfile"; + const content = `image:\n file: ${dockerFileName}\n`; const result = this.parser.parse(content); expect(result.config).to.deep.equal({ image: { - file: dockerFileName - } + file: dockerFileName, + }, }); } @test public testComplexImageWithContext() { - const dockerFileName = 'Dockerfile'; - const dockerContext = 'docker'; - const content = - `image:\n file: ${dockerFileName}\n context: ${dockerContext}\n`; + const dockerFileName = "Dockerfile"; + const dockerContext = "docker"; + const content = `image:\n file: ${dockerFileName}\n context: ${dockerContext}\n`; const result = this.parser.parse(content); expect(result.config).to.deep.equal({ image: { file: dockerFileName, - context: dockerContext - } + context: dockerContext, + }, }); } @test public testGitconfig() { - const content = -` + const content = ` gitConfig: core.autocrlf: input `; @@ -132,20 +127,19 @@ gitConfig: const result = this.parser.parse(content, {}, DEFAULT_CONFIG); expect(result.config).to.deep.equal({ gitConfig: { - "core.autocrlf": "input" + "core.autocrlf": "input", }, - image: DEFAULT_IMAGE + image: DEFAULT_IMAGE, }); } @test public testBrokenConfig() { - const content = - `image: 42\n`; + const content = `image: 42\n`; const result = this.parser.parse(content, {}, DEFAULT_CONFIG); expect(result.config).to.deep.equal({ - image: DEFAULT_IMAGE + image: DEFAULT_IMAGE, }); } } -module.exports = new TestGitpodFileParser() // Only to circumvent no usage warning :-/ +module.exports = new TestGitpodFileParser(); // Only to circumvent no usage warning :-/ diff --git a/components/gitpod-protocol/src/gitpod-file-parser.ts b/components/gitpod-protocol/src/gitpod-file-parser.ts index 3c498225d959e5..6591d9591aa8f1 100644 --- a/components/gitpod-protocol/src/gitpod-file-parser.ts +++ b/components/gitpod-protocol/src/gitpod-file-parser.ts @@ -7,40 +7,39 @@ import { injectable } from "inversify"; import * as yaml from "js-yaml"; import * as Ajv from "ajv"; -import { log } from './util/logging'; +import { log } from "./util/logging"; import { WorkspaceConfig, PortRangeConfig } from "./protocol"; -export type MaybeConfig = WorkspaceConfig | undefined +export type MaybeConfig = WorkspaceConfig | undefined; -const schema = require('../data/gitpod-schema.json'); +const schema = require("../data/gitpod-schema.json"); const validate = new Ajv().compile(schema); const defaultParseOptions = { acceptPortRanges: false, }; export interface ParseResult { - config: WorkspaceConfig - parsedConfig?: WorkspaceConfig - validationErrors?: string[] + config: WorkspaceConfig; + parsedConfig?: WorkspaceConfig; + validationErrors?: string[]; } @injectable() export class GitpodFileParser { - public parse(content: string, parseOptions = {}, defaultConfig: WorkspaceConfig = {}): ParseResult { const options = { - ...defaultParseOptions, - ...parseOptions, + ...defaultParseOptions, + ...parseOptions, }; try { const parsedConfig = yaml.safeLoad(content) as any; validate(parsedConfig); - const validationErrors = validate.errors ? validate.errors.map( e => e.message || e.keyword ) : undefined; + const validationErrors = validate.errors ? validate.errors.map((e) => e.message || e.keyword) : undefined; if (validationErrors && validationErrors.length > 0) { return { config: defaultConfig, parsedConfig, - validationErrors + validationErrors, }; } const overrides = {} as any; @@ -49,17 +48,17 @@ export class GitpodFileParser { } return { config: { - ... defaultConfig, - ... parsedConfig, - ... overrides, + ...defaultConfig, + ...parsedConfig, + ...overrides, }, - parsedConfig + parsedConfig, }; } catch (err) { - log.error('Unparsable Gitpod configuration', err, { content }); + log.error("Unparsable Gitpod configuration", err, { content }); return { config: defaultConfig, - validationErrors: ['Unparsable Gitpod configuration: ' + err.toString()] + validationErrors: ["Unparsable Gitpod configuration: " + err.toString()], }; } } diff --git a/components/gitpod-protocol/src/gitpod-service.ts b/components/gitpod-protocol/src/gitpod-service.ts index fff4e8e73a11bd..727fd53beed4fa 100644 --- a/components/gitpod-protocol/src/gitpod-service.ts +++ b/components/gitpod-protocol/src/gitpod-service.ts @@ -5,32 +5,52 @@ */ import { - User, WorkspaceInfo, WorkspaceCreationResult, WorkspaceInstanceUser, - WhitelistedRepository, WorkspaceImageBuild, AuthProviderInfo, CreateWorkspaceMode, - Token, UserEnvVarValue, Terms, - Configuration, UserInfo, GitpodTokenType, - GitpodToken, AuthProviderEntry, GuessGitTokenScopesParams, GuessedGitTokenScopes, ProjectEnvVar -} from './protocol'; + User, + WorkspaceInfo, + WorkspaceCreationResult, + WorkspaceInstanceUser, + WhitelistedRepository, + WorkspaceImageBuild, + AuthProviderInfo, + CreateWorkspaceMode, + Token, + UserEnvVarValue, + Terms, + Configuration, + UserInfo, + GitpodTokenType, + GitpodToken, + AuthProviderEntry, + GuessGitTokenScopesParams, + GuessedGitTokenScopes, + ProjectEnvVar, +} from "./protocol"; import { - Team, TeamMemberInfo, - TeamMembershipInvite, Project, TeamMemberRole, PrebuildWithStatus, StartPrebuildResult, PartialProject -} from './teams-projects-protocol'; -import { JsonRpcProxy, JsonRpcServer } from './messaging/proxy-factory'; -import { Disposable, CancellationTokenSource } from 'vscode-jsonrpc'; -import { HeadlessLogUrls } from './headless-workspace-log'; -import { WorkspaceInstance, WorkspaceInstancePort, WorkspaceInstancePhase } from './workspace-instance'; -import { AdminServer } from './admin-protocol'; -import { GitpodHostUrl } from './util/gitpod-host-url'; -import { WebSocketConnectionProvider } from './messaging/browser/connection'; -import { PermissionName } from './permission'; -import { LicenseService } from './license-protocol'; -import { Emitter } from './util/event'; -import { AccountStatement, CreditAlert } from './accounting-protocol'; -import { GithubUpgradeURL, PlanCoupon } from './payment-protocol'; -import { TeamSubscription, TeamSubscriptionSlot, TeamSubscriptionSlotResolved } from './team-subscription-protocol'; -import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from './analytics'; -import { IDEServer } from './ide-protocol'; -import { InstallationAdminSettings, TelemetryData } from './installation-admin-protocol'; + Team, + TeamMemberInfo, + TeamMembershipInvite, + Project, + TeamMemberRole, + PrebuildWithStatus, + StartPrebuildResult, + PartialProject, +} from "./teams-projects-protocol"; +import { JsonRpcProxy, JsonRpcServer } from "./messaging/proxy-factory"; +import { Disposable, CancellationTokenSource } from "vscode-jsonrpc"; +import { HeadlessLogUrls } from "./headless-workspace-log"; +import { WorkspaceInstance, WorkspaceInstancePort, WorkspaceInstancePhase } from "./workspace-instance"; +import { AdminServer } from "./admin-protocol"; +import { GitpodHostUrl } from "./util/gitpod-host-url"; +import { WebSocketConnectionProvider } from "./messaging/browser/connection"; +import { PermissionName } from "./permission"; +import { LicenseService } from "./license-protocol"; +import { Emitter } from "./util/event"; +import { AccountStatement, CreditAlert } from "./accounting-protocol"; +import { GithubUpgradeURL, PlanCoupon } from "./payment-protocol"; +import { TeamSubscription, TeamSubscriptionSlot, TeamSubscriptionSlotResolved } from "./team-subscription-protocol"; +import { RemotePageMessage, RemoteTrackMessage, RemoteIdentifyMessage } from "./analytics"; +import { IDEServer } from "./ide-protocol"; +import { InstallationAdminSettings, TelemetryData } from "./installation-admin-protocol"; export interface GitpodClient { onInstanceUpdate(instance: WorkspaceInstance): void; @@ -46,7 +66,7 @@ export interface GitpodClient { //#endregion } -export const GitpodServer = Symbol('GitpodServer'); +export const GitpodServer = Symbol("GitpodServer"); export interface GitpodServer extends JsonRpcServer, AdminServer, LicenseService, IDEServer { // User related API getLoggedInUser(): Promise; @@ -159,8 +179,8 @@ export interface GitpodServer extends JsonRpcServer, AdminServer, deleteProjectEnvironmentVariable(variableId: string): Promise; // content service - getContentBlobUploadUrl(name: string): Promise - getContentBlobDownloadUrl(name: string): Promise + getContentBlobUploadUrl(name: string): Promise; + getContentBlobDownloadUrl(name: string): Promise; // Gitpod token getGitpodTokens(): Promise; @@ -228,9 +248,13 @@ export interface GitpodServer extends JsonRpcServer, AdminServer, tsGet(): Promise; tsGetSlots(): Promise; - tsGetUnassignedSlot(teamSubscriptionId: string): Promise + tsGetUnassignedSlot(teamSubscriptionId: string): Promise; tsAddSlots(teamSubscriptionId: string, quantity: number): Promise; - tsAssignSlot(teamSubscriptionId: string, teamSubscriptionSlotId: string, identityStr: string | undefined): Promise + tsAssignSlot( + teamSubscriptionId: string, + teamSubscriptionSlotId: string, + identityStr: string | undefined, + ): Promise; tsReassignSlot(teamSubscriptionId: string, teamSubscriptionSlotId: string, newIdentityStr: string): Promise; tsDeactivateSlot(teamSubscriptionId: string, teamSubscriptionSlotId: string): Promise; tsReactivateSlot(teamSubscriptionId: string, teamSubscriptionSlotId: string): Promise; @@ -246,13 +270,13 @@ export interface GitpodServer extends JsonRpcServer, AdminServer, } export interface RateLimiterError { - method?: string, + method?: string; /** * Retry after this many seconds, earliest. * cmp.: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After */ - retryAfter: number, + retryAfter: number; } export interface CreateProjectParams { @@ -299,13 +323,17 @@ export interface ClientHeaderFields { export const WorkspaceTimeoutValues = ["30m", "60m", "180m"] as const; -export const createServiceMock = function (methods: Partial>): GitpodServiceImpl { +export const createServiceMock = function ( + methods: Partial>, +): GitpodServiceImpl { return new GitpodServiceImpl(createServerMock(methods)); -} +}; -export const createServerMock = function (methods: Partial>): JsonRpcProxy { - methods.setClient = methods.setClient || (() => { }); - methods.dispose = methods.dispose || (() => { }); +export const createServerMock = function ( + methods: Partial>, +): JsonRpcProxy { + methods.setClient = methods.setClient || (() => {}); + methods.dispose = methods.dispose || (() => {}); return new Proxy>(methods as any as JsonRpcProxy, { // @ts-ignore get: (target: S, property: keyof S) => { @@ -314,25 +342,25 @@ export const createServerMock = function = JsonRpcProxy; export class GitpodCompositeClient implements GitpodClient { @@ -406,8 +434,8 @@ export class GitpodCompositeClient implements Gitpo if (index > -1) { this.clients.splice(index, 1); } - } - } + }, + }; } onInstanceUpdate(instance: WorkspaceInstance): void { @@ -416,7 +444,7 @@ export class GitpodCompositeClient implements Gitpo try { client.onInstanceUpdate(instance); } catch (error) { - console.error(error) + console.error(error); } } } @@ -428,19 +456,22 @@ export class GitpodCompositeClient implements Gitpo try { client.onPrebuildUpdate(update); } catch (error) { - console.error(error) + console.error(error); } } } } - onWorkspaceImageBuildLogs(info: WorkspaceImageBuild.StateInfo, content: WorkspaceImageBuild.LogContent | undefined): void { + onWorkspaceImageBuildLogs( + info: WorkspaceImageBuild.StateInfo, + content: WorkspaceImageBuild.LogContent | undefined, + ): void { for (const client of this.clients) { if (client.onWorkspaceImageBuildLogs) { try { client.onWorkspaceImageBuildLogs(info, content); } catch (error) { - console.error(error) + console.error(error); } } } @@ -452,7 +483,7 @@ export class GitpodCompositeClient implements Gitpo try { client.notifyDidOpenConnection(); } catch (error) { - console.error(error) + console.error(error); } } } @@ -464,7 +495,7 @@ export class GitpodCompositeClient implements Gitpo try { client.notifyDidCloseConnection(); } catch (error) { - console.error(error) + console.error(error); } } } @@ -476,17 +507,16 @@ export class GitpodCompositeClient implements Gitpo try { client.onCreditAlert(creditAlert); } catch (error) { - console.error(error) + console.error(error); } } } } - } export type GitpodService = GitpodServiceImpl; -const hasWindow = (typeof window !== 'undefined'); +const hasWindow = typeof window !== "undefined"; const phasesOrder: Record = { unknown: 0, preparing: 1, @@ -496,44 +526,41 @@ const phasesOrder: Record = { running: 5, interrupted: 6, stopping: 7, - stopped: 8 + stopped: 8, }; export class WorkspaceInstanceUpdateListener { private readonly onDidChangeEmitter = new Emitter(); readonly onDidChange = this.onDidChangeEmitter.event; - private source: 'sync' | 'update' = 'sync'; + private source: "sync" | "update" = "sync"; get info(): WorkspaceInfo { return this._info; } - constructor( - private readonly service: GitpodService, - private _info: WorkspaceInfo - ) { + constructor(private readonly service: GitpodService, private _info: WorkspaceInfo) { service.registerClient({ - onInstanceUpdate: instance => { + onInstanceUpdate: (instance) => { if (this.isOutOfOrder(instance)) { return; } this.cancelSync(); this._info.latestInstance = instance; - this.source = 'update'; + this.source = "update"; this.onDidChangeEmitter.fire(undefined); }, notifyDidOpenConnection: () => { this.sync(); - } + }, }); if (hasWindow) { // learn about page lifecycle here: https://developers.google.com/web/updates/2018/07/page-lifecycle-api - window.document.addEventListener('visibilitychange', async () => { - if (window.document.visibilityState === 'visible') { + window.document.addEventListener("visibilitychange", async () => { + if (window.document.visibilityState === "visible") { this.sync(); } }); - window.addEventListener('pageshow', e => { + window.addEventListener("pageshow", (e) => { if (e.persisted) { this.sync(); } @@ -561,12 +588,12 @@ export class WorkspaceInstanceUpdateListener { return; } this._info = info; - this.source = 'sync'; + this.source = "sync"; this.onDidChangeEmitter.fire(undefined); } catch (e) { - console.error('failed to sync workspace instance:', e) + console.error("failed to sync workspace instance:", e); } - }) + }); } private cancelSync(): void { if (this.syncTokenSource) { @@ -583,7 +610,7 @@ export class WorkspaceInstanceUpdateListener { if (instance.workspaceId !== this._info.workspace.id) { return true; } - if (this.source === 'update') { + if (this.source === "update") { return false; } if (instance.id !== this.info.latestInstance?.id) { @@ -591,15 +618,13 @@ export class WorkspaceInstanceUpdateListener { } return phasesOrder[instance.status.phase] < phasesOrder[this.info.latestInstance.status.phase]; } - } export interface GitpodServiceOptions { - onReconnect?: () => (void | Promise) + onReconnect?: () => void | Promise; } export class GitpodServiceImpl { - private readonly compositeClient = new GitpodCompositeClient(); constructor(public readonly server: JsonRpcProxy, private options?: GitpodServiceOptions) { @@ -614,7 +639,8 @@ export class GitpodServiceImpl>(); listenToInstance(workspaceId: string): Promise { - const listener = this.instanceListeners.get(workspaceId) || + const listener = + this.instanceListeners.get(workspaceId) || (async () => { const info = await this.server.getWorkspace(workspaceId); return new WorkspaceInstanceUpdateListener(this, info); @@ -630,26 +656,25 @@ export class GitpodServiceImpl(serverUrl: string | Promise) { +export function createGitpodService( + serverUrl: string | Promise, +) { const toWsUrl = (serverUrl: string) => { - return new GitpodHostUrl(serverUrl) - .asWebsocket() - .withApi({ pathname: GitpodServerPath }) - .toString(); + return new GitpodHostUrl(serverUrl).asWebsocket().withApi({ pathname: GitpodServerPath }).toString(); }; let url: string | Promise; if (typeof serverUrl === "string") { url = toWsUrl(serverUrl); } else { - url = serverUrl.then(url => toWsUrl(url)); + url = serverUrl.then((url) => toWsUrl(url)); } const connectionProvider = new WebSocketConnectionProvider(); - let onReconnect = () => { }; + let onReconnect = () => {}; const gitpodServer = connectionProvider.createProxy(url, undefined, { - onListening: socket => { + onListening: (socket) => { onReconnect = () => socket.reconnect(); - } + }, }); return new GitpodServiceImpl(gitpodServer, { onReconnect }); -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/headless-workspace-log.ts b/components/gitpod-protocol/src/headless-workspace-log.ts index 0f00e5a5b06cfd..5e54e1a161e168 100644 --- a/components/gitpod-protocol/src/headless-workspace-log.ts +++ b/components/gitpod-protocol/src/headless-workspace-log.ts @@ -4,7 +4,6 @@ * See License-AGPL.txt in the project root for license information. */ - export enum HeadlessWorkspaceEventType { LogOutput = "log-output", FinishedSuccessfully = "finish-success", @@ -12,14 +11,16 @@ export enum HeadlessWorkspaceEventType { AbortedTimedOut = "aborted-timeout", Aborted = "aborted", Failed = "failed", - Started = "started" + Started = "started", } export namespace HeadlessWorkspaceEventType { export function isRunning(t: HeadlessWorkspaceEventType) { return t === HeadlessWorkspaceEventType.LogOutput; } export function didFinish(t: HeadlessWorkspaceEventType) { - return t === HeadlessWorkspaceEventType.FinishedButFailed || t === HeadlessWorkspaceEventType.FinishedSuccessfully; + return ( + t === HeadlessWorkspaceEventType.FinishedButFailed || t === HeadlessWorkspaceEventType.FinishedSuccessfully + ); } } @@ -36,4 +37,4 @@ export interface HeadlessLogUrls { /** cmp. @const HEADLESS_LOG_STREAM_STATUS_CODE_REGEX */ export const HEADLESS_LOG_STREAM_STATUS_CODE = "X-LogStream-StatusCode"; -export const HEADLESS_LOG_STREAM_STATUS_CODE_REGEX = /X-LogStream-StatusCode: ([0-9]{3})/; \ No newline at end of file +export const HEADLESS_LOG_STREAM_STATUS_CODE_REGEX = /X-LogStream-StatusCode: ([0-9]{3})/; diff --git a/components/gitpod-protocol/src/ide-frontend-service.ts b/components/gitpod-protocol/src/ide-frontend-service.ts index 566abcce8171ae..1714f37a4e7d24 100644 --- a/components/gitpod-protocol/src/ide-frontend-service.ts +++ b/components/gitpod-protocol/src/ide-frontend-service.ts @@ -7,7 +7,7 @@ import { Event } from "./util/event"; import { Disposable } from "./util/disposable"; -export type IDEFrontendState = 'init' | 'ready' | 'terminated'; +export type IDEFrontendState = "init" | "ready" | "terminated"; export interface IDEFrontendService { readonly state: IDEFrontendState; @@ -25,4 +25,4 @@ export interface IDEFrontendService { * confirmation dialogs for stopped workspaces. */ start(): Disposable; -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/ide-protocol.ts b/components/gitpod-protocol/src/ide-protocol.ts index db88d325ca7fd0..84f0ba06dd8301 100644 --- a/components/gitpod-protocol/src/ide-protocol.ts +++ b/components/gitpod-protocol/src/ide-protocol.ts @@ -45,12 +45,12 @@ export interface IDEClient { /** * Desktop IDEs supported by the client. */ - desktopIDEs?: string[] + desktopIDEs?: string[]; /** * Steps to install the client on user machine. */ - installationSteps?: string[] + installationSteps?: string[]; } export interface IDEOption { @@ -67,12 +67,12 @@ export interface IDEOption { /** * The type of the IDE, currently 'browser' or 'desktop'. */ - type: 'browser' | 'desktop'; + type: "browser" | "desktop"; /** - * The logo for the IDE. That could be a key in (see - * components/dashboard/src/images/ideLogos.ts) or a URL. - */ + * The logo for the IDE. That could be a key in (see + * components/dashboard/src/images/ideLogos.ts) or a URL. + */ logo: string; /** @@ -93,8 +93,8 @@ export interface IDEOption { notes?: string[]; /** - * If `true` this IDE option is not visible in the IDE preferences. - */ + * If `true` this IDE option is not visible in the IDE preferences. + */ hidden?: boolean; /** diff --git a/components/gitpod-protocol/src/index.ts b/components/gitpod-protocol/src/index.ts index 0997eef243b2f8..63174ac8fe02ea 100644 --- a/components/gitpod-protocol/src/index.ts +++ b/components/gitpod-protocol/src/index.ts @@ -4,19 +4,19 @@ * See License-AGPL.txt in the project root for license information. */ -export * from './protocol'; -export * from './gitpod-service'; -export * from './util/disposable'; -export * from './util/event'; -export * from './util/queue'; -export * from './license-protocol'; -export * from './workspace-instance'; -export * from './permission'; -export * from './admin-protocol'; -export * from './email-protocol'; -export * from './headless-workspace-log'; -export * from './context-url'; -export * from './teams-projects-protocol'; -export * from './snapshot-url'; -export * from './oss-allowlist'; -export * from './installation-admin-protocol'; +export * from "./protocol"; +export * from "./gitpod-service"; +export * from "./util/disposable"; +export * from "./util/event"; +export * from "./util/queue"; +export * from "./license-protocol"; +export * from "./workspace-instance"; +export * from "./permission"; +export * from "./admin-protocol"; +export * from "./email-protocol"; +export * from "./headless-workspace-log"; +export * from "./context-url"; +export * from "./teams-projects-protocol"; +export * from "./snapshot-url"; +export * from "./oss-allowlist"; +export * from "./installation-admin-protocol"; diff --git a/components/gitpod-protocol/src/installation-admin-protocol.ts b/components/gitpod-protocol/src/installation-admin-protocol.ts index 5d84bd1a4db6ab..adea2481ce6610 100644 --- a/components/gitpod-protocol/src/installation-admin-protocol.ts +++ b/components/gitpod-protocol/src/installation-admin-protocol.ts @@ -4,11 +4,11 @@ * See License-AGPL.txt in the project root for license information. */ -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; const InstallationAdminSettingsPrototype = { - sendTelemetry: true -} + sendTelemetry: true, +}; export type InstallationAdminSettings = typeof InstallationAdminSettingsPrototype; @@ -24,10 +24,10 @@ export interface InstallationAdmin { } export interface TelemetryData { - installationAdmin: InstallationAdmin - totalUsers: number - totalWorkspaces: number - totalInstances: number + installationAdmin: InstallationAdmin; + totalUsers: number; + totalWorkspaces: number; + totalInstances: number; } export namespace InstallationAdmin { @@ -36,7 +36,7 @@ export namespace InstallationAdmin { id: uuidv4(), settings: { ...InstallationAdminSettingsPrototype, - } + }, }; } } diff --git a/components/gitpod-protocol/src/license-protocol.ts b/components/gitpod-protocol/src/license-protocol.ts index 9dfc3bc5282bef..ce0042e655a1c3 100644 --- a/components/gitpod-protocol/src/license-protocol.ts +++ b/components/gitpod-protocol/src/license-protocol.ts @@ -4,11 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ - export interface LicenseValidationResult { - valid: boolean - msg?: string - issue?: LicenseIssue + valid: boolean; + msg?: string; + issue?: LicenseIssue; } export type LicenseIssue = "seats-exhausted"; @@ -34,5 +33,5 @@ export enum LicenseFeature { export interface LicenseService { validateLicense(): Promise; getLicenseInfo(): Promise; - licenseIncludesFeature(feature: LicenseFeature): Promise -} \ No newline at end of file + licenseIncludesFeature(feature: LicenseFeature): Promise; +} diff --git a/components/gitpod-protocol/src/messaging/browser/connection.ts b/components/gitpod-protocol/src/messaging/browser/connection.ts index bd2ef6f781c8bf..1ef3878449a806 100644 --- a/components/gitpod-protocol/src/messaging/browser/connection.ts +++ b/components/gitpod-protocol/src/messaging/browser/connection.ts @@ -11,7 +11,7 @@ import { AbstractMessageWriter } from "vscode-jsonrpc/lib/messageWriter"; import { AbstractMessageReader } from "vscode-jsonrpc/lib/messageReader"; import { JsonRpcProxyFactory, JsonRpcProxy } from "../proxy-factory"; import { ConnectionEventHandler, ConnectionHandler } from "../handler"; -import ReconnectingWebSocket, { Event } from 'reconnecting-websocket'; +import ReconnectingWebSocket, { Event } from "reconnecting-websocket"; export interface WebSocketOptions { onerror?: (event: Event) => void; @@ -19,7 +19,6 @@ export interface WebSocketOptions { } export class WebSocketConnectionProvider { - /** * Create a proxy object to remote interface of T type * over a web socket connection for the given path. @@ -27,27 +26,33 @@ export class WebSocketConnectionProvider { * An optional target can be provided to handle * notifications and requests from a remote side. */ - createProxy(path: string | Promise, target?: object, options?: WebSocketOptions): JsonRpcProxy { + createProxy( + path: string | Promise, + target?: object, + options?: WebSocketOptions, + ): JsonRpcProxy { const factory = new JsonRpcProxyFactory(target); const startListening = (path: string) => { - const socket = this.listen({ - path, - onConnection: c => factory.listen(c), - }, { - onTransportDidClose: () => factory.fireConnectionClosed(), - onTransportDidOpen: () => factory.fireConnectionOpened(), - }, - options + const socket = this.listen( + { + path, + onConnection: (c) => factory.listen(c), + }, + { + onTransportDidClose: () => factory.fireConnectionClosed(), + onTransportDidOpen: () => factory.fireConnectionOpened(), + }, + options, ); if (options?.onListening) { - options.onListening(socket as any as ReconnectingWebSocket) + options.onListening(socket as any as ReconnectingWebSocket); } }; if (typeof path === "string") { startListening(path); } else { - path.then(path => startListening(path)); + path.then((path) => startListening(path)); } return factory.createProxy(); } @@ -62,20 +67,15 @@ export class WebSocketConnectionProvider { const logger = this.createLogger(); if (options && options.onerror) { const onerror = options.onerror; - webSocket.addEventListener('error', (event) => { + webSocket.addEventListener("error", (event) => { onerror(event); }); } else { - webSocket.addEventListener('error', (error: Event) => { + webSocket.addEventListener("error", (error: Event) => { logger.error(JSON.stringify(error)); }); } - doListen( - webSocket as any as ReconnectingWebSocket, - handler, - eventHandler, - logger, - ); + doListen(webSocket as any as ReconnectingWebSocket, handler, eventHandler, logger); return webSocket; } @@ -93,10 +93,9 @@ export class WebSocketConnectionProvider { reconnectionDelayGrowFactor: 1.3, maxRetries: Infinity, debug: false, - WebSocket: WebSocket + WebSocket: WebSocket, }) as any; } - } // The following was extracted from vscode-ws-jsonrpc to make these changes: @@ -104,7 +103,12 @@ export class WebSocketConnectionProvider { // - webSocket.onopen: making sure it's only ever called once so we're re-using MessageConnection // - WebSocketMessageWriter: buffer and re-try messages instead of throwing an error immidiately // - WebSocketMessageReader: don't close MessageConnection on 'socket.onclose' -function doListen(resocket: ReconnectingWebSocket, handler: ConnectionHandler, eventHandler: ConnectionEventHandler, logger: Logger) { +function doListen( + resocket: ReconnectingWebSocket, + handler: ConnectionHandler, + eventHandler: ConnectionEventHandler, + logger: Logger, +) { resocket.addEventListener("close", () => eventHandler.onTransportDidClose()); let alreadyOpened = false; @@ -169,7 +173,7 @@ class BufferingWebSocketMessageWriter extends AbstractMessageWriter { protected flushBuffer() { if (this.buffer.length === 0) { - return + return; } const buffer = [...this.buffer]; @@ -186,26 +190,25 @@ class BufferingWebSocketMessageWriter extends AbstractMessageWriter { } } - /** * This takes vscode-ws-jsonrpc/lib/socket/reader/WebSocketMessageReader and removes the "onClose -> fireClose" connection */ class NonClosingWebSocketMessageReader extends AbstractMessageReader { protected readonly socket: IWebSocket; protected readonly events: any[] = []; - protected state: 'initial' | 'listening' | 'closed' = 'initial'; + protected state: "initial" | "listening" | "closed" = "initial"; protected callback: (message: any) => void = () => {}; constructor(socket: IWebSocket) { super(); this.socket = socket; - this.socket.onMessage(message => this.readMessage(message)); - this.socket.onError(error => this.fireError(error)); + this.socket.onMessage((message) => this.readMessage(message)); + this.socket.onError((error) => this.fireError(error)); this.socket.onClose((code, reason) => { if (code !== 1000) { const error = { - name: '' + code, - message: `Error during socket reconnect: code = ${code}, reason = ${reason}` + name: "" + code, + message: `Error during socket reconnect: code = ${code}, reason = ${reason}`, }; this.fireError(error); } @@ -213,47 +216,42 @@ class NonClosingWebSocketMessageReader extends AbstractMessageReader { }); } listen(callback: (message: any) => void) { - if (this.state === 'initial') { - this.state = 'listening'; + if (this.state === "initial") { + this.state = "listening"; this.callback = callback; while (this.events.length !== 0) { const event = this.events.pop(); if (event.message) { this.readMessage(event.message); - } - else if (event.error) { + } else if (event.error) { this.fireError(event.error); - } - else { + } else { this.fireClose(); } } } } readMessage(message: any) { - if (this.state === 'initial') { + if (this.state === "initial") { this.events.splice(0, 0, { message }); - } - else if (this.state === 'listening') { + } else if (this.state === "listening") { const data = JSON.parse(message); this.callback(data); } } fireError(error: any) { - if (this.state === 'initial') { + if (this.state === "initial") { this.events.splice(0, 0, { error }); - } - else if (this.state === 'listening') { + } else if (this.state === "listening") { super.fireError(error); } } fireClose() { - if (this.state === 'initial') { + if (this.state === "initial") { this.events.splice(0, 0, {}); - } - else if (this.state === 'listening') { + } else if (this.state === "listening") { super.fireClose(); } - this.state = 'closed'; + this.state = "closed"; } } diff --git a/components/gitpod-protocol/src/messaging/browser/window-connection.ts b/components/gitpod-protocol/src/messaging/browser/window-connection.ts index b95f40e7d5f500..5070ae872a026b 100644 --- a/components/gitpod-protocol/src/messaging/browser/window-connection.ts +++ b/components/gitpod-protocol/src/messaging/browser/window-connection.ts @@ -4,23 +4,27 @@ * See License-AGPL.txt in the project root for license information. */ -import { Message } from 'vscode-jsonrpc/lib/messages'; -import { AbstractMessageWriter, MessageWriter } from 'vscode-jsonrpc/lib/messageWriter'; -import { AbstractMessageReader, MessageReader, DataCallback } from 'vscode-jsonrpc/lib/messageReader'; -import { MessageConnection, createMessageConnection } from 'vscode-jsonrpc/lib/main'; -import { ConsoleLogger } from 'vscode-ws-jsonrpc'; +import { Message } from "vscode-jsonrpc/lib/messages"; +import { AbstractMessageWriter, MessageWriter } from "vscode-jsonrpc/lib/messageWriter"; +import { AbstractMessageReader, MessageReader, DataCallback } from "vscode-jsonrpc/lib/messageReader"; +import { MessageConnection, createMessageConnection } from "vscode-jsonrpc/lib/main"; +import { ConsoleLogger } from "vscode-ws-jsonrpc"; interface WindowMessage extends Message { - serviceId: string + serviceId: string; } function isWindowMessage(value: any): value is WindowMessage { - return !!value && typeof value === 'object' && - ('jsonrpc' in value && typeof value['jsonrpc'] === 'string') && - ('serviceId' in value && typeof value['serviceId'] === 'string'); + return ( + !!value && + typeof value === "object" && + "jsonrpc" in value && + typeof value["jsonrpc"] === "string" && + "serviceId" in value && + typeof value["serviceId"] === "string" + ); } export class WindowMessageWriter extends AbstractMessageWriter implements MessageWriter { - constructor(readonly serviceId: string, readonly window: Window, readonly targetOrigin: string) { super(); } @@ -29,44 +33,50 @@ export class WindowMessageWriter extends AbstractMessageWriter implements Messag const { serviceId } = this; this.window.postMessage(Object.assign(msg, { serviceId }), this.targetOrigin); } - } export class WindowMessageReader extends AbstractMessageReader implements MessageReader { - protected callback?: DataCallback; protected readonly buffer: Message[] = []; constructor(readonly serviceId: string, readonly sourceOrigin: string) { super(); - window.addEventListener('message', event => { - if (this.sourceOrigin !== '*' && event.origin !== this.sourceOrigin) { - return; - } - if (!isWindowMessage(event.data) || event.data.serviceId !== this.serviceId) { - return; - } - if (this.callback) { - this.callback(event.data); - } else { - this.buffer.push(event.data); - } - }, false); + window.addEventListener( + "message", + (event) => { + if (this.sourceOrigin !== "*" && event.origin !== this.sourceOrigin) { + return; + } + if (!isWindowMessage(event.data) || event.data.serviceId !== this.serviceId) { + return; + } + if (this.callback) { + this.callback(event.data); + } else { + this.buffer.push(event.data); + } + }, + false, + ); } listen(callback: DataCallback): void { let message; - while (message = this.buffer.pop()) { + while ((message = this.buffer.pop())) { callback(message); } Object.freeze(this.buffer); this.callback = callback; } - } -export function createWindowMessageConnection(serviceId: string, window: Window, sourceOrigin: string, targetOrigin = sourceOrigin): MessageConnection { +export function createWindowMessageConnection( + serviceId: string, + window: Window, + sourceOrigin: string, + targetOrigin = sourceOrigin, +): MessageConnection { const reader = new WindowMessageReader(serviceId, sourceOrigin); const writer = new WindowMessageWriter(serviceId, window, targetOrigin); return createMessageConnection(reader, writer, new ConsoleLogger()); -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/messaging/client-call-metrics.ts b/components/gitpod-protocol/src/messaging/client-call-metrics.ts index 9d2d90762e0775..f8a858980a071e 100644 --- a/components/gitpod-protocol/src/messaging/client-call-metrics.ts +++ b/components/gitpod-protocol/src/messaging/client-call-metrics.ts @@ -4,94 +4,93 @@ * See License-AGPL.txt in the project root for license information. */ -import { injectable } from 'inversify'; -import * as prometheusClient from 'prom-client'; +import { injectable } from "inversify"; +import * as prometheusClient from "prom-client"; -type GrpcMethodType = 'unary' | 'client_stream' | 'server_stream' | 'bidi_stream'; +type GrpcMethodType = "unary" | "client_stream" | "server_stream" | "bidi_stream"; export interface IGrpcCallMetricsLabels { - service: string, - method: string, - type: GrpcMethodType, + service: string; + method: string; + type: GrpcMethodType; } export interface IGrpcCallMetricsLabelsWithCode extends IGrpcCallMetricsLabels { - code: string + code: string; } export const IClientCallMetrics = Symbol("IClientCallMetrics"); export interface IClientCallMetrics { - started(labels: IGrpcCallMetricsLabels) : void; - sent(labels: IGrpcCallMetricsLabels) : void; - received(labels: IGrpcCallMetricsLabels) : void; - handled(labels: IGrpcCallMetricsLabelsWithCode) : void; + started(labels: IGrpcCallMetricsLabels): void; + sent(labels: IGrpcCallMetricsLabels): void; + received(labels: IGrpcCallMetricsLabels): void; + handled(labels: IGrpcCallMetricsLabelsWithCode): void; } @injectable() export class PrometheusClientCallMetrics implements IClientCallMetrics { + readonly startedCounter: prometheusClient.Counter; + readonly sentCounter: prometheusClient.Counter; + readonly receivedCounter: prometheusClient.Counter; + readonly handledCounter: prometheusClient.Counter; - readonly startedCounter: prometheusClient.Counter; - readonly sentCounter: prometheusClient.Counter; - readonly receivedCounter: prometheusClient.Counter; - readonly handledCounter: prometheusClient.Counter; + constructor() { + this.startedCounter = new prometheusClient.Counter({ + name: "grpc_client_started_total", + help: "Total number of RPCs started on the client.", + labelNames: ["grpc_service", "grpc_method", "grpc_type"], + registers: [prometheusClient.register], + }); + this.sentCounter = new prometheusClient.Counter({ + name: "grpc_client_msg_sent_total", + help: " Total number of gRPC stream messages sent by the client.", + labelNames: ["grpc_service", "grpc_method", "grpc_type"], + registers: [prometheusClient.register], + }); + this.receivedCounter = new prometheusClient.Counter({ + name: "grpc_client_msg_received_total", + help: "Total number of RPC stream messages received by the client.", + labelNames: ["grpc_service", "grpc_method", "grpc_type"], + registers: [prometheusClient.register], + }); + this.handledCounter = new prometheusClient.Counter({ + name: "grpc_client_handled_total", + help: "Total number of RPCs completed by the client, regardless of success or failure.", + labelNames: ["grpc_service", "grpc_method", "grpc_type", "grpc_code"], + registers: [prometheusClient.register], + }); + } - constructor() { - this.startedCounter = new prometheusClient.Counter({ - name: 'grpc_client_started_total', - help: 'Total number of RPCs started on the client.', - labelNames: ['grpc_service', 'grpc_method', 'grpc_type'], - registers: [prometheusClient.register] - }); - this.sentCounter = new prometheusClient.Counter({ - name: 'grpc_client_msg_sent_total', - help: ' Total number of gRPC stream messages sent by the client.', - labelNames: ['grpc_service', 'grpc_method', 'grpc_type'], - registers: [prometheusClient.register] - }); - this.receivedCounter = new prometheusClient.Counter({ - name: 'grpc_client_msg_received_total', - help: 'Total number of RPC stream messages received by the client.', - labelNames: ['grpc_service', 'grpc_method', 'grpc_type'], - registers: [prometheusClient.register] - }); - this.handledCounter = new prometheusClient.Counter({ - name: 'grpc_client_handled_total', - help: 'Total number of RPCs completed by the client, regardless of success or failure.', - labelNames: ['grpc_service', 'grpc_method', 'grpc_type', 'grpc_code'], - registers: [prometheusClient.register] - }); - } + started(labels: IGrpcCallMetricsLabels): void { + this.startedCounter.inc({ + grpc_service: labels.service, + grpc_method: labels.method, + grpc_type: labels.type, + }); + } - started(labels: IGrpcCallMetricsLabels): void { - this.startedCounter.inc({ - grpc_service: labels.service, - grpc_method: labels.method, - grpc_type: labels.type - }); - } + sent(labels: IGrpcCallMetricsLabels): void { + this.sentCounter.inc({ + grpc_service: labels.service, + grpc_method: labels.method, + grpc_type: labels.type, + }); + } - sent(labels: IGrpcCallMetricsLabels): void { - this.sentCounter.inc({ - grpc_service: labels.service, - grpc_method: labels.method, - grpc_type: labels.type - }); - } + received(labels: IGrpcCallMetricsLabels): void { + this.receivedCounter.inc({ + grpc_service: labels.service, + grpc_method: labels.method, + grpc_type: labels.type, + }); + } - received(labels: IGrpcCallMetricsLabels): void { - this.receivedCounter.inc({ - grpc_service: labels.service, - grpc_method: labels.method, - grpc_type: labels.type - }); - } - - handled(labels: IGrpcCallMetricsLabelsWithCode): void { - this.handledCounter.inc({ - grpc_service: labels.service, - grpc_method: labels.method, - grpc_type: labels.type, - grpc_code: labels.code - }); - } + handled(labels: IGrpcCallMetricsLabelsWithCode): void { + this.handledCounter.inc({ + grpc_service: labels.service, + grpc_method: labels.method, + grpc_type: labels.type, + grpc_code: labels.code, + }); + } } diff --git a/components/gitpod-protocol/src/messaging/error.ts b/components/gitpod-protocol/src/messaging/error.ts index b825a012eabeb7..755279a6df6d7b 100644 --- a/components/gitpod-protocol/src/messaging/error.ts +++ b/components/gitpod-protocol/src/messaging/error.ts @@ -4,8 +4,6 @@ * See License-AGPL.txt in the project root for license information. */ - - export namespace ErrorCodes { // 401 Unauthorized export const NOT_AUTHENTICATED = 401; @@ -81,4 +79,4 @@ export namespace ErrorCodes { // 640 Headless logs are not available (yet) export const HEADLESS_LOG_NOT_YET_AVAILABLE = 640; -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/messaging/handler.ts b/components/gitpod-protocol/src/messaging/handler.ts index e17b91de26e940..364734ef0e7a8e 100644 --- a/components/gitpod-protocol/src/messaging/handler.ts +++ b/components/gitpod-protocol/src/messaging/handler.ts @@ -7,7 +7,7 @@ import { MessageConnection } from "vscode-jsonrpc"; -export const ConnectionHandler = Symbol('ConnectionHandler'); +export const ConnectionHandler = Symbol("ConnectionHandler"); export interface ConnectionHandler { readonly path: string; @@ -18,10 +18,10 @@ export interface ConnectionEventHandler { /** * Called when the transport underpinning the connection got closed */ - onTransportDidClose(): void; + onTransportDidClose(): void; - /** - * Called when the transport underpinning the connection is (re-)opened - */ - onTransportDidOpen(): void; + /** + * Called when the transport underpinning the connection is (re-)opened + */ + onTransportDidOpen(): void; } diff --git a/components/gitpod-protocol/src/messaging/node/connection.ts b/components/gitpod-protocol/src/messaging/node/connection.ts index 9a36a34175b6b7..1493ac8f232c31 100644 --- a/components/gitpod-protocol/src/messaging/node/connection.ts +++ b/components/gitpod-protocol/src/messaging/node/connection.ts @@ -10,7 +10,7 @@ import { IWebSocket } from "vscode-ws-jsonrpc"; export function toIWebSocket(ws: ws) { return { - send: content => { + send: (content) => { if (ws.readyState >= ws.CLOSING) { // ws is already CLOSING/CLOSED, send() would just return an error. return; @@ -18,23 +18,23 @@ export function toIWebSocket(ws: ws) { // in general send-errors should trigger an 'error' event already, we just make sure it actually happens. try { - ws.send(content, err => { + ws.send(content, (err) => { if (!err) { return; } - ws.emit('error', err); + ws.emit("error", err); }); } catch (err) { - ws.emit('error', err); + ws.emit("error", err); } }, - onMessage: cb => ws.on('message', cb), - onError: cb => ws.on('error', cb), - onClose: cb => ws.on('close', cb), + onMessage: (cb) => ws.on("message", cb), + onError: (cb) => ws.on("error", cb), + onClose: (cb) => ws.on("close", cb), dispose: () => { if (ws.readyState < ws.CLOSING) { ws.close(); } - } + }, }; } diff --git a/components/gitpod-protocol/src/messaging/proxy-factory.ts b/components/gitpod-protocol/src/messaging/proxy-factory.ts index aae18d12ccb3cd..b39181f965f8a5 100644 --- a/components/gitpod-protocol/src/messaging/proxy-factory.ts +++ b/components/gitpod-protocol/src/messaging/proxy-factory.ts @@ -8,8 +8,8 @@ import { MessageConnection, ResponseError } from "vscode-jsonrpc"; import { Event, Emitter } from "../util/event"; import { Disposable } from "../util/disposable"; -import { ConnectionHandler } from './handler'; -import { log } from '../util/logging'; +import { ConnectionHandler } from "./handler"; +import { log } from "../util/logging"; export type JsonRpcServer = Disposable & { /** @@ -27,10 +27,7 @@ export interface JsonRpcConnectionEventEmitter { export type JsonRpcProxy = T & JsonRpcConnectionEventEmitter; export class JsonRpcConnectionHandler implements ConnectionHandler { - constructor( - readonly path: string, - readonly targetFactory: (proxy: JsonRpcProxy, request?: object) => any - ) { } + constructor(readonly path: string, readonly targetFactory: (proxy: JsonRpcProxy, request?: object) => any) {} onConnection(connection: MessageConnection, request?: object): void { const factory = new JsonRpcProxyFactory(); @@ -83,7 +80,6 @@ export class JsonRpcConnectionHandler implements ConnectionHan * @param - The type of the object to expose to JSON-RPC. */ export class JsonRpcProxyFactory implements ProxyHandler { - protected readonly onDidOpenConnectionEmitter = new Emitter(); protected readonly onDidCloseConnectionEmitter = new Emitter(); @@ -101,17 +97,15 @@ export class JsonRpcProxyFactory implements ProxyHandler { } protected waitForConnection(): void { - this.connectionPromise = new Promise(resolve => - this.connectionPromiseResolve = resolve - ); - this.connectionPromise.then(connection => { + this.connectionPromise = new Promise((resolve) => (this.connectionPromiseResolve = resolve)); + this.connectionPromise.then((connection) => { connection.onClose(() => this.fireConnectionClosed()); this.fireConnectionOpened(); }); } fireConnectionClosed() { - this.onDidCloseConnectionEmitter.fire(undefined) + this.onDidCloseConnectionEmitter.fire(undefined); } fireConnectionOpened() { @@ -204,35 +198,34 @@ export class JsonRpcProxyFactory implements ProxyHandler { * @returns A callable that executes the JSON-RPC call. */ get(target: T, p: PropertyKey, receiver: any): any { - if (p === 'setClient') { + if (p === "setClient") { return (client: any) => { this.target = client; }; } - if (p === 'onDidOpenConnection') { + if (p === "onDidOpenConnection") { return this.onDidOpenConnectionEmitter.event; } - if (p === 'onDidCloseConnection') { + if (p === "onDidCloseConnection") { return this.onDidCloseConnectionEmitter.event; } const isNotify = this.isNotification(p); return (...args: any[]) => - this.connectionPromise.then(connection => - new Promise((resolve, reject) => { - try { - if (isNotify) { - connection.sendNotification(p.toString(), ...args); - resolve(undefined); - } else { - const resultPromise = connection.sendRequest(p.toString(), ...args) as Promise; - resultPromise - .catch((err: any) => reject(err)) - .then((result: any) => resolve(result)); + this.connectionPromise.then( + (connection) => + new Promise((resolve, reject) => { + try { + if (isNotify) { + connection.sendNotification(p.toString(), ...args); + resolve(undefined); + } else { + const resultPromise = connection.sendRequest(p.toString(), ...args) as Promise; + resultPromise.catch((err: any) => reject(err)).then((result: any) => resolve(result)); + } + } catch (err) { + reject(err); } - } catch (err) { - reject(err); - } - }) + }), ); } diff --git a/components/gitpod-protocol/src/oss-allowlist.ts b/components/gitpod-protocol/src/oss-allowlist.ts index 05c8c29b000e6a..207e73522a533a 100644 --- a/components/gitpod-protocol/src/oss-allowlist.ts +++ b/components/gitpod-protocol/src/oss-allowlist.ts @@ -9,7 +9,7 @@ export interface OssAllowList { * A string that identifies a GitHub/GitLab/Bitbucket identity of the form "/" * E.g., "github.com/geropl" */ - identity: string, + identity: string; - deleted?: boolean, -} \ No newline at end of file + deleted?: boolean; +} diff --git a/components/gitpod-protocol/src/payment-protocol.ts b/components/gitpod-protocol/src/payment-protocol.ts index c8207dcef00874..c83310f972411d 100644 --- a/components/gitpod-protocol/src/payment-protocol.ts +++ b/components/gitpod-protocol/src/payment-protocol.ts @@ -4,8 +4,8 @@ * See License-AGPL.txt in the project root for license information. */ - export namespace PaymentProtocol { - export const UPDATE_GITPOD_SUBSCRIPTION_PATH = '/payment/chargebee' +export namespace PaymentProtocol { + export const UPDATE_GITPOD_SUBSCRIPTION_PATH = "/payment/chargebee"; } export interface PlanCoupon { diff --git a/components/gitpod-protocol/src/permission.ts b/components/gitpod-protocol/src/permission.ts index f18c0fcfb11e04..35253c54fad298 100644 --- a/components/gitpod-protocol/src/permission.ts +++ b/components/gitpod-protocol/src/permission.ts @@ -4,11 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ - // see below for explanation export const Permissions = { - "monitor": undefined, - "enforcement": undefined, + monitor: undefined, + enforcement: undefined, "privileged-ws": undefined, "registry-access": undefined, "admin-users": undefined, @@ -18,21 +17,20 @@ export const Permissions = { "ide-settings": undefined, "new-workspace-cluster": undefined, }; -export type PermissionName = keyof (typeof Permissions); -export const Roles = {"devops": undefined, "viewer": undefined, "admin": undefined }; -export type RoleName = keyof (typeof Roles); +export type PermissionName = keyof typeof Permissions; +export const Roles = { devops: undefined, viewer: undefined, admin: undefined }; +export type RoleName = keyof typeof Roles; export type RoleOrPermission = RoleName | PermissionName; export namespace RoleName { export const is = (o: any): o is RoleName => { - return typeof(o) === 'string' - && Role.all().some(r => r.name === o); - } + return typeof o === "string" && Role.all().some((r) => r.name === o); + }; } export interface Role { - name: RoleName, - permissions: PermissionName[], + name: RoleName; + permissions: PermissionName[]; } export namespace RolesOrPermissions { @@ -44,11 +42,11 @@ export namespace RolesOrPermissions { if (Permission.is(rop)) { permissions.add(rop); } else if (RoleName.is(rop)) { - Role.getByName(rop).permissions.forEach(p => permissions.add(p)); + Role.getByName(rop).permissions.forEach((p) => permissions.add(p)); } } return permissions; - }; + } } export namespace Permission { @@ -77,9 +75,8 @@ export namespace Permission { export const IDE_SETTINGS: PermissionName = "ide-settings"; export const is = (o: any): o is PermissionName => { - return typeof(o) === 'string' - && Permission.all().some(p => p === o); - } + return typeof o === "string" && Permission.all().some((p) => p === o); + }; export const all = (): PermissionName[] => { return Object.keys(Permissions) as PermissionName[]; @@ -90,21 +87,13 @@ export namespace Role { /** The default role for all Gitpod developers */ export const DEVOPS: Role = { name: "devops", - permissions: [ - Permission.MONITOR, - Permission.ENFORCEMENT, - Permission.REGISTRY_ACCESS, - Permission.IDE_SETTINGS - ] + permissions: [Permission.MONITOR, Permission.ENFORCEMENT, Permission.REGISTRY_ACCESS, Permission.IDE_SETTINGS], }; /** A role for people that are allowed to view Gitpod internals */ export const VIEWER: Role = { name: "viewer", - permissions: [ - Permission.MONITOR, - Permission.REGISTRY_ACCESS, - ] + permissions: [Permission.MONITOR, Permission.REGISTRY_ACCESS], }; export const ADMIN: Role = { @@ -115,11 +104,11 @@ export namespace Role { Permission.ADMIN_PROJECTS, Permission.ADMIN_API, Permission.ENFORCEMENT, - ] - } + ], + }; export const getByName = (name: RoleName): Role => { - const result = Role.all().find(r => r.name === name) + const result = Role.all().find((r) => r.name === name); if (!result) { throw Error("Unknown RoleName: " + name); } @@ -128,7 +117,7 @@ export namespace Role { export const all = (): Role[] => { return Object.keys(Role) - .map(k => (Role as any)[k]) - .filter(k => typeof(k) === 'object'); + .map((k) => (Role as any)[k]) + .filter((k) => typeof k === "object"); }; } diff --git a/components/gitpod-protocol/src/plans.ts b/components/gitpod-protocol/src/plans.ts index a8b8b8f1bf2dc9..bcbfca91bf6422 100644 --- a/components/gitpod-protocol/src/plans.ts +++ b/components/gitpod-protocol/src/plans.ts @@ -4,43 +4,53 @@ * See License-AGPL.txt in the project root for license information. */ -export type Currency = 'USD' | 'EUR'; +export type Currency = "USD" | "EUR"; export namespace Currency { export const getAll = (): Currency[] => { - return ['USD', 'EUR']; - } + return ["USD", "EUR"]; + }; export const getSymbol = (c: Currency) => { - return c === 'USD' ? '$' : '€'; - } + return c === "USD" ? "$" : "€"; + }; } /** * Different plans of the same type MAY have different prices ($/€, for example) but MUST have the same feature set. */ -export type PlanType = 'free' | 'free-50' | 'free-open-source' | 'student' | 'basic' | 'personal' | 'professional' | 'professional-new'; -export type HoursPerMonthType = number | 'unlimited'; +export type PlanType = + | "free" + | "free-50" + | "free-open-source" + | "student" + | "basic" + | "personal" + | "professional" + | "professional-new"; +export type HoursPerMonthType = number | "unlimited"; export interface Plan { - chargebeeId: string - githubId?: number - githubPlanNumber?: number + chargebeeId: string; + githubId?: number; + githubPlanNumber?: number; - name: string - currency: Currency + name: string; + currency: Currency; /** In full currencies (Euro, US Dollar, ...) */ - pricePerMonth: number - hoursPerMonth: HoursPerMonthType - type: PlanType - team?: boolean + pricePerMonth: number; + hoursPerMonth: HoursPerMonthType; + type: PlanType; + team?: boolean; } export namespace Plan { export const is = (o: any): o is Plan => { - return 'chargebeeId' in o - && 'name' in o - && 'currency' in o - && 'pricePerMonth' in o - && 'hoursPerMonth' in o - && 'type' in o; - } + return ( + "chargebeeId" in o && + "name" in o && + "currency" in o && + "pricePerMonth" in o && + "hoursPerMonth" in o && + "type" in o + ); + }; } export const MAX_PARALLEL_WORKSPACES = 16; @@ -51,24 +61,24 @@ export interface Coupon { } export namespace Coupon { export const is = (o: any): o is Coupon => { - return 'id' in o; - } + return "id" in o; + }; } export namespace Coupons { export const INTERNAL_GITPOD_GHSP: Coupon = { id: "INTERNAL_GITPOD_GHSP", - isGithubStudentCoupon: true + isGithubStudentCoupon: true, }; export const INTERNAL_GITPOD_GHSP_2: Coupon = { id: "INTERNAL_GITPOD_GHSP_2", - isGithubStudentCoupon: true + isGithubStudentCoupon: true, }; export const GITHUBSTUDENTPACKFORFACULTY: Coupon = { id: "GITHUBSTUDENTPACKFORFACULTY", - isGithubStudentCoupon: true + isGithubStudentCoupon: true, }; export const isGithubStudentCoupon = (id: string): boolean | undefined => { - const c = getAllCoupons().find(ic => ic.id === id); + const c = getAllCoupons().find((ic) => ic.id === id); if (!c) { return undefined; } @@ -76,8 +86,8 @@ export namespace Coupons { }; export const getAllCoupons = (): Coupon[] => { return Object.keys(Coupons) - .map(k => (Coupons as any)[k]) - .filter(a => typeof a === 'object' && Coupon.is(a)); + .map((k) => (Coupons as any)[k]) + .filter((a) => typeof a === "object" && Coupon.is(a)); }; } @@ -116,151 +126,151 @@ export namespace Plans { * The old default plan (v1): 100h hours for public repos */ export const FREE: Plan = { - chargebeeId: 'free', + chargebeeId: "free", githubId: 2034, githubPlanNumber: 1, - type: 'free', - name: 'Open Source', - currency: 'USD', + type: "free", + name: "Open Source", + currency: "USD", pricePerMonth: 0, - hoursPerMonth: 100 + hoursPerMonth: 100, }; /** * The new default plan (v3): 50h hours for public repos */ export const FREE_50: Plan = { - chargebeeId: 'free-50', + chargebeeId: "free-50", githubId: 4902, githubPlanNumber: 5, - type: 'free-50', - name: 'Open Source', - currency: 'USD', + type: "free-50", + name: "Open Source", + currency: "USD", pricePerMonth: 0, - hoursPerMonth: 50 + hoursPerMonth: 50, }; /** * Users created after this date get the FREE_50 plan (v3) instead of the (old) FREE plan (v1) */ - export const FREE_50_START_DATE = '2019-12-19T00:00:00.000Z'; + export const FREE_50_START_DATE = "2019-12-19T00:00:00.000Z"; /** * The 'Professional Open Source' plan was introduced to offer professional open-souce developers unlimited hours. */ export const FREE_OPEN_SOURCE: Plan = { - chargebeeId: 'free-open-source', - type: 'free-open-source', - name: 'Professional Open Source', - currency: 'USD', + chargebeeId: "free-open-source", + type: "free-open-source", + name: "Professional Open Source", + currency: "USD", pricePerMonth: 0, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** * The 'Student Unleashed' plans were introduced to give students access to the highly-priced unlimited plans. */ export const PROFESSIONAL_STUDENT_EUR: Plan = { - chargebeeId: 'professional-student-eur', - type: 'student', - name: 'Student Unleashed', - currency: 'EUR', + chargebeeId: "professional-student-eur", + type: "student", + name: "Student Unleashed", + currency: "EUR", pricePerMonth: 8, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** * The 'Student Unleashed' plans were introduced to give students access to the highly-priced unlimited plans. */ export const PROFESSIONAL_STUDENT_USD: Plan = { - chargebeeId: 'professional-student-usd', - type: 'student', - name: 'Student Unleashed', - currency: 'USD', + chargebeeId: "professional-student-usd", + type: "student", + name: "Student Unleashed", + currency: "USD", pricePerMonth: 9, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** * The 'Student Unleashed' plans were introduced to give students access to the highly-priced unlimited plans. */ export const TEAM_PROFESSIONAL_STUDENT_EUR: Plan = { - chargebeeId: 'team-professional-student-eur', - type: 'student', - name: 'Team Student Unleashed', + chargebeeId: "team-professional-student-eur", + type: "student", + name: "Team Student Unleashed", team: true, - currency: 'EUR', + currency: "EUR", pricePerMonth: 8, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** * The 'Student Unleashed' plans were introduced to give students access to the highly-priced unlimited plans. */ export const TEAM_PROFESSIONAL_STUDENT_USD: Plan = { - chargebeeId: 'team-professional-student-usd', - type: 'student', - name: 'Team Student Unleashed', + chargebeeId: "team-professional-student-usd", + type: "student", + name: "Team Student Unleashed", team: true, - currency: 'USD', + currency: "USD", pricePerMonth: 9, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** * The 'basic' plan was the original differentiator between FREE and Professional (v1) but got discarded soon. */ export const BASIC_EUR: Plan = { - chargebeeId: 'basic-eur', - type: 'basic', - name: 'Standard', - currency: 'EUR', + chargebeeId: "basic-eur", + type: "basic", + name: "Standard", + currency: "EUR", pricePerMonth: 17, - hoursPerMonth: 100 + hoursPerMonth: 100, }; /** * The 'basic' plan was the original differentiator between FREE and Professional (v1) but got discarded soon. */ export const BASIC_USD: Plan = { - chargebeeId: 'basic-usd', + chargebeeId: "basic-usd", githubId: 2035, githubPlanNumber: 2, - type: 'basic', - name: 'Standard', - currency: 'USD', + type: "basic", + name: "Standard", + currency: "USD", pricePerMonth: 19, - hoursPerMonth: 100 + hoursPerMonth: 100, }; /** * The 'personal' plan was introduced to superseed the 'basic' plan (introduced with v2) to be more attractive to hobbyists. */ export const PERSONAL_EUR: Plan = { - chargebeeId: 'personal-eur', - type: 'personal', - name: 'Personal', - currency: 'EUR', + chargebeeId: "personal-eur", + type: "personal", + name: "Personal", + currency: "EUR", pricePerMonth: 8, - hoursPerMonth: 100 + hoursPerMonth: 100, }; /** * The 'personal' plan was introduced to superseed the 'basic' plan (introduced with v2) to be more attractive to hobbyists. */ export const PERSONAL_USD: Plan = { - chargebeeId: 'personal-usd', + chargebeeId: "personal-usd", githubId: 2274, githubPlanNumber: 4, - type: 'personal', - name: 'Personal', - currency: 'USD', + type: "personal", + name: "Personal", + currency: "USD", pricePerMonth: 9, - hoursPerMonth: 100 + hoursPerMonth: 100, }; /** @@ -268,12 +278,12 @@ export namespace Plans { * Unleashed (39$/35€) on the right. */ export const PROFESSIONAL_NEW_EUR: Plan = { - chargebeeId: 'professional-new-eur', - type: 'professional-new', - name: 'Professional', - currency: 'EUR', + chargebeeId: "professional-new-eur", + type: "professional-new", + name: "Professional", + currency: "EUR", pricePerMonth: 23, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** @@ -281,12 +291,12 @@ export namespace Plans { * Unleashed (39$/35€) on the right. */ export const PROFESSIONAL_NEW_USD: Plan = { - chargebeeId: 'professional-new-usd', - type: 'professional-new', - name: 'Professional', - currency: 'USD', + chargebeeId: "professional-new-usd", + type: "professional-new", + name: "Professional", + currency: "USD", pricePerMonth: 25, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** @@ -294,13 +304,13 @@ export namespace Plans { * Unleashed (39$/35€) on the right. */ export const TEAM_PROFESSIONAL_NEW_EUR: Plan = { - chargebeeId: 'team-professional-new-eur', - type: 'professional-new', - name: 'Team Professional', - currency: 'EUR', + chargebeeId: "team-professional-new-eur", + type: "professional-new", + name: "Team Professional", + currency: "EUR", team: true, pricePerMonth: 23, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** @@ -308,13 +318,13 @@ export namespace Plans { * Unleashed (39$/35€) on the right. */ export const TEAM_PROFESSIONAL_NEW_USD: Plan = { - chargebeeId: 'team-professional-new-usd', - type: 'professional-new', - name: 'Team Professional', - currency: 'USD', + chargebeeId: "team-professional-new-usd", + type: "professional-new", + name: "Team Professional", + currency: "USD", team: true, pricePerMonth: 25, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** @@ -322,12 +332,12 @@ export namespace Plans { * It was originally introduced as 'Professional', and we cannot update the ids, so it stays that way in the code. */ export const PROFESSIONAL_EUR: Plan = { - chargebeeId: 'professional-eur', - type: 'professional', - name: 'Unleashed', - currency: 'EUR', + chargebeeId: "professional-eur", + type: "professional", + name: "Unleashed", + currency: "EUR", pricePerMonth: 35, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** @@ -335,15 +345,15 @@ export namespace Plans { * It was originally introduced as 'Professional', and we cannot update the ids, so it stays that way in the code. */ export const PROFESSIONAL_USD: Plan = { - chargebeeId: 'professional-usd', + chargebeeId: "professional-usd", githubId: 2036, githubPlanNumber: 3, - type: 'professional', - name: 'Unleashed', - currency: 'USD', + type: "professional", + name: "Unleashed", + currency: "USD", pricePerMonth: 39, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** @@ -351,13 +361,13 @@ export namespace Plans { * It was originally introduced as 'Professional', and we cannot update the ids, so it stays that way in the code. */ export const TEAM_PROFESSIONAL_USD: Plan = { - chargebeeId: 'team-professional-usd', - type: 'professional', - name: 'Team Unleashed', - currency: 'USD', + chargebeeId: "team-professional-usd", + type: "professional", + name: "Team Unleashed", + currency: "USD", team: true, pricePerMonth: 39, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; /** @@ -365,22 +375,21 @@ export namespace Plans { * It was originally introduced as 'Professional', and we cannot update the ids, so it stays that way in the code. */ export const TEAM_PROFESSIONAL_EUR: Plan = { - chargebeeId: 'team-professional-eur', - type: 'professional', - name: 'Team Unleashed', - currency: 'EUR', + chargebeeId: "team-professional-eur", + type: "professional", + name: "Team Unleashed", + currency: "EUR", team: true, pricePerMonth: 35, - hoursPerMonth: 'unlimited' + hoursPerMonth: "unlimited", }; const getAllPlans = (): Plan[] => { return Object.keys(Plans) - .map(k => (Plans as any)[k]) - .filter(a => typeof a === 'object' && Plan.is(a)); + .map((k) => (Plans as any)[k]) + .filter((a) => typeof a === "object" && Plan.is(a)); }; - /** * This function returns all individual plans that might be active (= we have subscriptions for) at the moment */ @@ -393,67 +402,71 @@ export namespace Plans { Plans.PROFESSIONAL_NEW_EUR, Plans.PROFESSIONAL_NEW_USD, Plans.PROFESSIONAL_EUR, - Plans.PROFESSIONAL_USD + Plans.PROFESSIONAL_USD, ]; - return [ - Plans.FREE, - Plans.FREE_50, - Plans.FREE_OPEN_SOURCE, - ...availablePaidPlans.filter(p => p.currency) - ] - }; + return [Plans.FREE, Plans.FREE_50, Plans.FREE_OPEN_SOURCE, ...availablePaidPlans.filter((p) => p.currency)]; + } export const getAvailableTeamPlans = (currency?: Currency): Plan[] => { - const teamPlans = getAllPlans().filter(p => !!p.team); - return currency ? teamPlans.filter(p => p.currency === currency) : teamPlans; + const teamPlans = getAllPlans().filter((p) => !!p.team); + return currency ? teamPlans.filter((p) => p.currency === currency) : teamPlans; }; export function getById(id: string | undefined): Plan | undefined { if (id === undefined) { return undefined; } - return getAllPlans() - .find(p => p.chargebeeId === id) || undefined; - }; + return getAllPlans().find((p) => p.chargebeeId === id) || undefined; + } export function getByTypeAndCurrency(type: PlanType, currency: Currency): Plan | undefined { return getAllPlans() - .filter(p => p.type) - .find(p => p.currency === currency); + .filter((p) => p.type) + .find((p) => p.currency === currency); } export function getProPlan(currency: Currency): Plan { switch (currency) { - case "EUR": return Plans.PROFESSIONAL_EUR; - case "USD": return Plans.PROFESSIONAL_USD; + case "EUR": + return Plans.PROFESSIONAL_EUR; + case "USD": + return Plans.PROFESSIONAL_USD; } } export function getNewProPlan(currency: Currency): Plan { switch (currency) { - case "EUR": return Plans.PROFESSIONAL_NEW_EUR; - case "USD": return Plans.PROFESSIONAL_NEW_USD; + case "EUR": + return Plans.PROFESSIONAL_NEW_EUR; + case "USD": + return Plans.PROFESSIONAL_NEW_USD; } } export function getStudentProPlan(currency: Currency): Plan { switch (currency) { - case "EUR": return Plans.PROFESSIONAL_STUDENT_EUR; - case "USD": return Plans.PROFESSIONAL_STUDENT_USD; + case "EUR": + return Plans.PROFESSIONAL_STUDENT_EUR; + case "USD": + return Plans.PROFESSIONAL_STUDENT_USD; } } export function getBasicPlan(currency: Currency): Plan { switch (currency) { - case "EUR": return Plans.BASIC_EUR; - case "USD": return Plans.BASIC_USD; + case "EUR": + return Plans.BASIC_EUR; + case "USD": + return Plans.BASIC_USD; } } export function getPersonalPlan(currency: Currency): Plan { switch (currency) { - case "EUR": return Plans.PERSONAL_EUR; - case "USD": return Plans.PERSONAL_USD; + case "EUR": + return Plans.PERSONAL_EUR; + case "USD": + return Plans.PERSONAL_USD; } } @@ -462,9 +475,11 @@ export namespace Plans { } export function isFreePlan(chargebeeId: string | undefined): boolean { - return chargebeeId === Plans.FREE.chargebeeId - || chargebeeId === Plans.FREE_50.chargebeeId - || chargebeeId === Plans.FREE_OPEN_SOURCE.chargebeeId; + return ( + chargebeeId === Plans.FREE.chargebeeId || + chargebeeId === Plans.FREE_50.chargebeeId || + chargebeeId === Plans.FREE_OPEN_SOURCE.chargebeeId + ); } export function isFreeNonTransientPlan(chargebeeId: string | undefined): boolean { @@ -472,7 +487,7 @@ export namespace Plans { } export function getHoursPerMonth(plan: Plan): number { - return plan.hoursPerMonth == 'unlimited' ? ABSOLUTE_MAX_USAGE : plan.hoursPerMonth; + return plan.hoursPerMonth == "unlimited" ? ABSOLUTE_MAX_USAGE : plan.hoursPerMonth; } /** @@ -504,7 +519,6 @@ export namespace Plans { return DEFAULT; } - /** * This declares the plan structure we have in Gitpod: All entries in a sub-array have the same arity in this structure. * This is used to impose a partial order on plan types (cmp. compareTypes(...)). @@ -515,7 +529,7 @@ export namespace Plans { ["free-50", "free", "free-open-source"], ["personal", "basic"], ["professional-new"], - ["professional", "student"] + ["professional", "student"], ]; function getPlanTypeArity(type: PlanType) { @@ -571,60 +585,93 @@ export namespace Plans { export const getFeaturesFor = (p: Plan): Feature[] => { switch (p.type) { case "free": - return [ - { title: 'Public repositories' } - ]; + return [{ title: "Public repositories" }]; case "free-50": - return [ - { title: 'Public repositories' } - ]; + return [{ title: "Public repositories" }]; case "free-open-source": - return [ - { title: 'Public repositories' }, - ]; + return [{ title: "Public repositories" }]; case "student": return [ - { title: 'Private & Public repos' }, - { title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, tooltip: 'The number of workspaces running at the same time' }, - { title: 'Team Manageable', link: "/teams/", tooltip: 'Setup Gitpod for an entire Team with a single invoice and credit card' }, - { title: '1h Timeout', tooltip: 'Workspaces without user activity are stopped after 1 hour' }, - { title: '3h Timeout Boost', tooltip: 'You can manually boost the timeout to 3 hours within a running workspace' } + { title: "Private & Public repos" }, + { + title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, + tooltip: "The number of workspaces running at the same time", + }, + { + title: "Team Manageable", + link: "/teams/", + tooltip: "Setup Gitpod for an entire Team with a single invoice and credit card", + }, + { title: "1h Timeout", tooltip: "Workspaces without user activity are stopped after 1 hour" }, + { + title: "3h Timeout Boost", + tooltip: "You can manually boost the timeout to 3 hours within a running workspace", + }, ]; case "basic": return [ - { title: 'Private & Public repos' }, - { title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, tooltip: 'The number of workspaces running at the same time.' }, + { title: "Private & Public repos" }, + { + title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, + tooltip: "The number of workspaces running at the same time.", + }, ]; // Personal case "personal": return [ - { title: 'Private & Public repos' }, - { title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, tooltip: 'The number of workspaces running at the same time' }, - { title: '30min Timeout', tooltip: 'Workspaces without user activity are stopped after 30 minutes' } + { title: "Private & Public repos" }, + { + title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, + tooltip: "The number of workspaces running at the same time", + }, + { + title: "30min Timeout", + tooltip: "Workspaces without user activity are stopped after 30 minutes", + }, ]; // Professional case "professional-new": return [ - { title: 'Private & Public repos' }, - { title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, tooltip: 'The number of workspaces running at the same time' }, - { title: 'Team Manageable', link: "/teams/", tooltip: 'Setup Gitpod for an entire Team with a single invoice and credit card' }, - { title: '30min Timeout', tooltip: 'Workspaces without user activity are stopped after 30 minutes' } + { title: "Private & Public repos" }, + { + title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, + tooltip: "The number of workspaces running at the same time", + }, + { + title: "Team Manageable", + link: "/teams/", + tooltip: "Setup Gitpod for an entire Team with a single invoice and credit card", + }, + { + title: "30min Timeout", + tooltip: "Workspaces without user activity are stopped after 30 minutes", + }, ]; // Unleashed case "professional": return [ - { title: 'Private & Public repos' }, - { title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, tooltip: 'The number of workspaces running at the same time' }, - { title: 'Team Manageable', link: "/teams/", tooltip: 'Setup Gitpod for an entire Team with a single invoice and credit card' }, - { title: '1h Timeout', tooltip: 'Workspaces without user activity are stopped after 1 hour' }, - { title: '3h Timeout Boost', tooltip: 'You can manually boost the timeout to 3 hours within a running workspace' } + { title: "Private & Public repos" }, + { + title: `${Plans.getParallelWorkspaces(p)} Parallel Workspaces`, + tooltip: "The number of workspaces running at the same time", + }, + { + title: "Team Manageable", + link: "/teams/", + tooltip: "Setup Gitpod for an entire Team with a single invoice and credit card", + }, + { title: "1h Timeout", tooltip: "Workspaces without user activity are stopped after 1 hour" }, + { + title: "3h Timeout Boost", + tooltip: "You can manually boost the timeout to 3 hours within a running workspace", + }, ]; } }; diff --git a/components/gitpod-protocol/src/protocol.ts b/components/gitpod-protocol/src/protocol.ts index a909814ed9bba1..0a9ea6455b8547 100644 --- a/components/gitpod-protocol/src/protocol.ts +++ b/components/gitpod-protocol/src/protocol.ts @@ -10,24 +10,24 @@ import { Project } from "./teams-projects-protocol"; import { createHash } from "crypto"; export interface UserInfo { - name?: string + name?: string; } export interface User { /** The user id */ - id: string + id: string; /** The timestamp when the user entry was created */ - creationDate: string + creationDate: string; - avatarUrl?: string + avatarUrl?: string; - name?: string + name?: string; /** Optional for backwards compatibility */ - fullName?: string + fullName?: string; - identities: Identity[] + identities: Identity[]; /** * Whether the user has been blocked to use our service, because of TOS violation for example. @@ -49,29 +49,27 @@ export interface User { export namespace User { export function is(data: any): data is User { - return data - && data.hasOwnProperty('id') - && data.hasOwnProperty('identities') + return data && data.hasOwnProperty("id") && data.hasOwnProperty("identities"); } export function getIdentity(user: User, authProviderId: string): Identity | undefined { - return user.identities.find(id => id.authProviderId === authProviderId); + return user.identities.find((id) => id.authProviderId === authProviderId); } export function censor(user: User): User { const res = { ...user }; - delete (res.additionalData); - res.identities = res.identities.map(i => { - delete (i.tokens); + delete res.additionalData; + res.identities = res.identities.map((i) => { + delete i.tokens; // The user field is not in the Identity shape, but actually exists on DBIdentity. // Trying to push this object out via JSON RPC will fail because of the cyclic nature // of this field. - delete ((i as any).user); + delete (i as any).user; return i; }); return res; } export function getPrimaryEmail(user: User): string { - const identities = user.identities.filter(i => !!i.primaryEmail); + const identities = user.identities.filter((i) => !!i.primaryEmail); if (identities.length <= 0) { throw new Error(`No identity with primary email for user: ${user.id}!`); } @@ -99,10 +97,10 @@ export interface AdditionalUserData { featurePreview?: boolean; ideSettings?: IDESettings; // key is the name of the news, string the iso date when it was seen - whatsNewSeen?: { [key: string]: string } + whatsNewSeen?: { [key: string]: string }; // key is the name of the OAuth client i.e. local app, string the iso date when it was approved // TODO(rl): provide a management UX to allow rescinding of approval - oauthClientsApproved?: { [key: string]: string } + oauthClientsApproved?: { [key: string]: string }; // to remember GH Orgs the user installed/updated the GH App for knownGitHubOrgs?: string[]; @@ -117,11 +115,11 @@ export interface EmailNotificationSettings { } export type IDESettings = { - defaultIde?: string - useDesktopIde?: boolean - defaultDesktopIde?: string - useLatestVersion?: boolean -} + defaultIde?: string; + useDesktopIde?: boolean; + defaultDesktopIde?: string; + useLatestVersion?: boolean; +}; export interface UserPlatform { uid: string; @@ -153,8 +151,8 @@ export interface UserFeatureSettings { * The values of this type MUST MATCH enum values in WorkspaceFeatureFlag from ws-manager/client/core_pb.d.ts * If they don't we'll break things during workspace startup. */ -export const WorkspaceFeatureFlags = { "full_workspace_backup": undefined, "fixed_resources": undefined }; -export type NamedWorkspaceFeatureFlag = keyof (typeof WorkspaceFeatureFlags); +export const WorkspaceFeatureFlags = { full_workspace_backup: undefined, fixed_resources: undefined }; +export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags; export interface EnvVarWithValue { name: string; @@ -167,7 +165,7 @@ export interface ProjectEnvVarWithValue extends EnvVarWithValue { censored: boolean; } -export type ProjectEnvVar = Omit; +export type ProjectEnvVar = Omit; export interface UserEnvVarValue extends EnvVarWithValue { id?: string; @@ -180,7 +178,6 @@ export interface UserEnvVar extends UserEnvVarValue { } export namespace UserEnvVar { - // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322 export function normalizeRepoPattern(pattern: string) { return pattern.toLocaleLowerCase(); @@ -200,7 +197,7 @@ export namespace UserEnvVar { if (repoPattern == "*") { score += 1; } - if (ownerPattern == '*') { + if (ownerPattern == "*") { score += 2; } if (ownerPattern == "#" || repoPattern == "#") { @@ -211,20 +208,20 @@ export namespace UserEnvVar { // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322 export function filter(vars: T[], owner: string, repo: string): T[] { - let result = vars.filter(e => { + let result = vars.filter((e) => { const [ownerPattern, repoPattern] = splitRepositoryPattern(e.repositoryPattern); - if (ownerPattern !== '*' && ownerPattern !== '#' && (!!owner && ownerPattern !== owner.toLocaleLowerCase())) { + if (ownerPattern !== "*" && ownerPattern !== "#" && !!owner && ownerPattern !== owner.toLocaleLowerCase()) { return false; } - if (repoPattern !== '*' && repoPattern !== '#' && (!!repo && repoPattern !== repo.toLocaleLowerCase())) { + if (repoPattern !== "*" && repoPattern !== "#" && !!repo && repoPattern !== repo.toLocaleLowerCase()) { return false; } return true; }); const resmap = new Map(); - result.forEach(e => { - const l = (resmap.get(e.name) || []); + result.forEach((e) => { + const l = resmap.get(e.name) || []; l.push(e); resmap.set(e.name, l); }); @@ -259,46 +256,45 @@ export namespace UserEnvVar { // DEPRECATED: Use ProjectEnvVar instead of repositoryPattern - https://github.com/gitpod-com/gitpod/issues/5322 export function splitRepositoryPattern(repositoryPattern: string): string[] { - const patterns = repositoryPattern.split('/'); - const repoPattern = patterns.slice(1).join('/') + const patterns = repositoryPattern.split("/"); + const repoPattern = patterns.slice(1).join("/"); const ownerPattern = patterns[0]; return [ownerPattern, repoPattern]; } } export interface GitpodToken { - /** Hash value (SHA256) of the token (primary key). */ - tokenHash: string + tokenHash: string; /** Human readable name of the token */ - name?: string + name?: string; /** Token kind */ - type: GitpodTokenType + type: GitpodTokenType; /** The user the token belongs to. */ - user: User + user: User; /** Scopes (e.g. limition to read-only) */ - scopes: string[] + scopes: string[]; /** Created timestamp */ - created: string + created: string; // token is deleted on the database and about to be collected by db-sync - deleted?: boolean + deleted?: boolean; } export enum GitpodTokenType { API_AUTH_TOKEN = 0, - MACHINE_AUTH_TOKEN = 1 + MACHINE_AUTH_TOKEN = 1, } export interface OneTimeSecret { - id: string + id: string; - value: string + value: string; expirationTime: string; @@ -330,13 +326,12 @@ export type IdentityLookup = Pick; export namespace Identity { export function is(data: any): data is Identity { - return data.hasOwnProperty('authProviderId') - && data.hasOwnProperty('authId') - && data.hasOwnProperty('authName') + return ( + data.hasOwnProperty("authProviderId") && data.hasOwnProperty("authId") && data.hasOwnProperty("authName") + ); } export function equals(id1: IdentityLookup, id2: IdentityLookup) { - return id1.authProviderId === id2.authProviderId - && id1.authId === id2.authId + return id1.authProviderId === id2.authProviderId && id1.authId === id2.authId; } } @@ -400,7 +395,7 @@ export interface Snapshot { message?: string; } -export type SnapshotState = 'pending' | 'available' | 'error'; +export type SnapshotState = "pending" | "available" | "error"; export interface LayoutData { workspaceId: string; @@ -429,13 +424,13 @@ export interface Workspace { * The resolved, fix name of the workspace image. We only use this * to access the logs during an image build. */ - imageNameResolved?: string + imageNameResolved?: string; /** * The resolved/built fixed named of the base image. This field is only set if the workspace * already has its base image built. */ - baseImageNameResolved?: string + baseImageNameResolved?: string; shareable?: boolean; pinned?: boolean; @@ -472,17 +467,16 @@ export type WorkspaceSoftDeletion = "user" | "gc"; export type WorkspaceType = "regular" | "prebuild" | "probe"; export namespace Workspace { - export function getFullRepositoryName(ws: Workspace): string | undefined { if (CommitContext.is(ws.context)) { - return ws.context.repository.owner + '/' + ws.context.repository.name + return ws.context.repository.owner + "/" + ws.context.repository.name; } return undefined; } export function getFullRepositoryUrl(ws: Workspace): string | undefined { if (CommitContext.is(ws.context)) { - return `https://${ws.context.repository.host}/${getFullRepositoryName(ws)}` + return `https://${ws.context.repository.host}/${getFullRepositoryName(ws)}`; } return undefined; } @@ -517,21 +511,21 @@ export namespace Workspace { } export interface GuessGitTokenScopesParams { - host: string - repoUrl: string - gitCommand: string - currentToken: GitToken + host: string; + repoUrl: string; + gitCommand: string; + currentToken: GitToken; } export interface GitToken { - token: string - user: string - scopes: string[] + token: string; + user: string; + scopes: string[]; } export interface GuessedGitTokenScopes { - message?: string - scopes?: string[] + message?: string; + scopes?: string[]; } export interface VSCodeConfig { @@ -568,7 +562,7 @@ export interface WorkspaceConfig { * additional-content - config comes from additional content, usually provided through the project's configuration * default - our static catch-all default config */ - _origin?: 'repo' | 'project-db' | 'definitely-gp' | 'derived' | 'additional-content' | 'default'; + _origin?: "repo" | "project-db" | "definitely-gp" | "derived" | "additional-content" | "default"; /** * Set of automatically infered feature flags. That's not something the user can set, but @@ -578,34 +572,33 @@ export interface WorkspaceConfig { } export interface GithubAppConfig { - prebuilds?: GithubAppPrebuildConfig + prebuilds?: GithubAppPrebuildConfig; } export interface GithubAppPrebuildConfig { - master?: boolean - branches?: boolean - pullRequests?: boolean - pullRequestsFromForks?: boolean - addCheck?: boolean | 'prevent-merge-on-error' - addBadge?: boolean - addLabel?: boolean | string - addComment?: boolean + master?: boolean; + branches?: boolean; + pullRequests?: boolean; + pullRequestsFromForks?: boolean; + addCheck?: boolean | "prevent-merge-on-error"; + addBadge?: boolean; + addLabel?: boolean | string; + addComment?: boolean; } export namespace GithubAppPrebuildConfig { export function is(obj: boolean | GithubAppPrebuildConfig): obj is GithubAppPrebuildConfig { - return !(typeof obj === 'boolean'); + return !(typeof obj === "boolean"); } } export type WorkspaceImageSource = WorkspaceImageSourceDocker | WorkspaceImageSourceReference; export interface WorkspaceImageSourceDocker { - dockerFilePath: string - dockerFileHash: string - dockerFileSource?: Commit + dockerFilePath: string; + dockerFileHash: string; + dockerFileSource?: Commit; } export namespace WorkspaceImageSourceDocker { export function is(obj: object): obj is WorkspaceImageSourceDocker { - return 'dockerFileHash' in obj - && 'dockerFilePath' in obj; + return "dockerFileHash" in obj && "dockerFilePath" in obj; } } export interface WorkspaceImageSourceReference { @@ -614,13 +607,13 @@ export interface WorkspaceImageSourceReference { } export namespace WorkspaceImageSourceReference { export function is(obj: object): obj is WorkspaceImageSourceReference { - return 'baseImageResolved' in obj; + return "baseImageResolved" in obj; } } -export type PrebuiltWorkspaceState +export type PrebuiltWorkspaceState = // the prebuild is queued and may start at anytime - = "queued" + | "queued" // the workspace prebuild is currently running (i.e. there's a workspace pod deployed) | "building" // the prebuild was aborted @@ -647,7 +640,7 @@ export interface PrebuiltWorkspace { export namespace PrebuiltWorkspace { export function isDone(pws: PrebuiltWorkspace) { - return pws.state === "available" || pws.state === "timeout" || pws.state === 'aborted'; + return pws.state === "available" || pws.state === "timeout" || pws.state === "aborted"; } export function isAvailable(pws: PrebuiltWorkspace) { @@ -675,13 +668,13 @@ export interface PrebuiltWorkspaceUpdatable { } export interface WhitelistedRepository { - url: string - name: string - description?: string - avatar?: string + url: string; + name: string; + description?: string; + avatar?: string; } -export type PortOnOpen = 'open-browser' | 'open-preview' | 'notify' | 'ignore'; +export type PortOnOpen = "open-browser" | "open-preview" | "notify" | "ignore"; export interface PortConfig { port: number; @@ -692,7 +685,7 @@ export interface PortConfig { } export namespace PortConfig { export function is(config: any): config is PortConfig { - return config && ('port' in config) && (typeof config.port === 'number'); + return config && "port" in config && typeof config.port === "number"; } } @@ -702,7 +695,7 @@ export interface PortRangeConfig { } export namespace PortRangeConfig { export function is(config: any): config is PortRangeConfig { - return config && ('port' in config) && (typeof config.port === 'string' || config.port instanceof String); + return config && "port" in config && (typeof config.port === "string" || config.port instanceof String); } } @@ -713,32 +706,31 @@ export interface TaskConfig { prebuild?: string; command?: string; env?: { [env: string]: any }; - openIn?: 'bottom' | 'main' | 'left' | 'right'; - openMode?: 'split-top' | 'split-left' | 'split-right' | 'split-bottom' | 'tab-before' | 'tab-after'; + openIn?: "bottom" | "main" | "left" | "right"; + openMode?: "split-top" | "split-left" | "split-right" | "split-bottom" | "tab-before" | "tab-after"; } export namespace TaskConfig { export function is(config: any): config is TaskConfig { - return config - && ('command' in config || 'init' in config || 'before' in config); + return config && ("command" in config || "init" in config || "before" in config); } } export namespace WorkspaceImageBuild { - export type Phase = 'BaseImage' | 'GitpodLayer' | 'Error' | 'Done'; + export type Phase = "BaseImage" | "GitpodLayer" | "Error" | "Done"; export interface StateInfo { - phase: Phase - currentStep?: number - maxSteps?: number + phase: Phase; + currentStep?: number; + maxSteps?: number; } export interface LogContent { - text: string - upToLine?: number - isDiff?: boolean + text: string; + upToLine?: number; + isDiff?: boolean; } export type LogCallback = (info: StateInfo, content: LogContent | undefined) => void; export namespace LogLine { - export const DELIMITER = '\r\n'; + export const DELIMITER = "\r\n"; export const DELIMITER_REGEX = /\r?\n/; } } @@ -747,20 +739,18 @@ export type ImageConfig = ImageConfigString | ImageConfigFile; export type ImageConfigString = string; export namespace ImageConfigString { export function is(config: ImageConfig | undefined): config is ImageConfigString { - return typeof config === 'string'; + return typeof config === "string"; } - } export interface ImageConfigFile { // Path to the Dockerfile relative to repository root - file: string, + file: string; // Path to the docker build context relative to repository root - context?: string + context?: string; } export namespace ImageConfigFile { export function is(config: ImageConfig | undefined): config is ImageConfigFile { - return typeof config === 'object' - && 'file' in config; + return typeof config === "object" && "file" in config; } } export interface ExternalImageConfigFile extends ImageConfigFile { @@ -768,9 +758,7 @@ export interface ExternalImageConfigFile extends ImageConfigFile { } export namespace ExternalImageConfigFile { export function is(config: any | undefined): config is ExternalImageConfigFile { - return typeof config === 'object' - && 'file' in config - && 'externalSource' in config; + return typeof config === "object" && "file" in config && "externalSource" in config; } } @@ -784,8 +772,7 @@ export interface WorkspaceContext { export namespace WorkspaceContext { export function is(context: any): context is WorkspaceContext { - return context - && 'title' in context; + return context && "title" in context; } } @@ -794,8 +781,7 @@ export interface WithSnapshot { } export namespace WithSnapshot { export function is(context: any): context is WithSnapshot { - return context - && 'snapshotBucketId' in context; + return context && "snapshotBucketId" in context; } } @@ -805,10 +791,7 @@ export interface WithPrebuild extends WithSnapshot { } export namespace WithPrebuild { export function is(context: any): context is WithPrebuild { - return context - && WithSnapshot.is(context) - && 'prebuildWorkspaceId' in context - && 'wasPrebuilt' in context; + return context && WithSnapshot.is(context) && "prebuildWorkspaceId" in context && "wasPrebuilt" in context; } } @@ -822,16 +805,14 @@ export interface WithDefaultConfig { export namespace WithDefaultConfig { export function is(context: any): context is WithDefaultConfig { - return context - && 'withDefaultConfig' in context - && context.withDefaultConfig; + return context && "withDefaultConfig" in context && context.withDefaultConfig; } export function mark(ctx: WorkspaceContext): WorkspaceContext & WithDefaultConfig { return { ...ctx, - withDefaultConfig: true - } + withDefaultConfig: true, + }; } } @@ -841,9 +822,7 @@ export interface SnapshotContext extends WorkspaceContext, WithSnapshot { export namespace SnapshotContext { export function is(context: any): context is SnapshotContext { - return context - && WithSnapshot.is(context) - && 'snapshotId' in context; + return context && WithSnapshot.is(context) && "snapshotId" in context; } } @@ -860,8 +839,7 @@ export interface StartPrebuildContext extends WorkspaceContext { export namespace StartPrebuildContext { export function is(context: any): context is StartPrebuildContext { - return context - && 'actual' in context; + return context && "actual" in context; } } @@ -873,21 +851,18 @@ export interface PrebuiltWorkspaceContext extends WorkspaceContext { export namespace PrebuiltWorkspaceContext { export function is(context: any): context is PrebuiltWorkspaceContext { - return context - && 'originalContext' in context - && 'prebuiltWorkspace' in context; + return context && "originalContext" in context && "prebuiltWorkspace" in context; } } export interface WithReferrerContext extends WorkspaceContext { - referrer: string - referrerIde?: string + referrer: string; + referrerIde?: string; } export namespace WithReferrerContext { export function is(context: any): context is WithReferrerContext { - return context - && 'referrer' in context; + return context && "referrer" in context; } } @@ -897,21 +872,18 @@ export interface WithEnvvarsContext extends WorkspaceContext { export namespace WithEnvvarsContext { export function is(context: any): context is WithEnvvarsContext { - return context - && 'envvars' in context + return context && "envvars" in context; } } export interface WorkspaceProbeContext extends WorkspaceContext { - responseURL: string - responseToken: string + responseURL: string; + responseToken: string; } export namespace WorkspaceProbeContext { export function is(context: any): context is WorkspaceProbeContext { - return context - && 'responseURL' in context - && 'responseToken' in context; + return context && "responseURL" in context && "responseToken" in context; } } @@ -923,32 +895,30 @@ export namespace RefType { } // This fallback is meant to handle the cases where (for historic reasons) ref is present but refType is missing return commit.refType || "branch"; - } + }; } export interface Commit { - repository: Repository - revision: string + repository: Repository; + revision: string; // Might contain either a branch or a tag (determined by refType) - ref?: string + ref?: string; // refType is only set if ref is present (and not for old workspaces, before this feature was added) - refType?: RefType + refType?: RefType; } export interface AdditionalContentContext extends WorkspaceContext { - /** * utf-8 encoded contents that will be copied on top of the workspace's filesystem */ - additionalFiles: {[filePath: string]: string}; - + additionalFiles: { [filePath: string]: string }; } export namespace AdditionalContentContext { export function is(ctx: any): ctx is AdditionalContentContext { - return 'additionalFiles' in ctx; + return "additionalFiles" in ctx; } export function hasDockerConfig(ctx: any, config: WorkspaceConfig): boolean { @@ -958,7 +928,7 @@ export namespace AdditionalContentContext { export interface CommitContext extends WorkspaceContext, GitCheckoutInfo { /** @deprecated Moved to .repository.cloneUrl, left here for backwards-compatibility for old workspace contextes in the DB */ - cloneUrl?: string + cloneUrl?: string; /** * The clone and checkout information for additional repositories in case of multi-repo projects. @@ -967,7 +937,6 @@ export interface CommitContext extends WorkspaceContext, GitCheckoutInfo { } export namespace CommitContext { - /** * Creates a hash for all the commits of the CommitContext and all sub-repo commit infos. * The hash is max 255 chars long. @@ -976,17 +945,19 @@ export namespace CommitContext { */ export function computeHash(commitContext: CommitContext): string { // for single commits we use the revision to be backward compatible. - if (!commitContext.additionalRepositoryCheckoutInfo || commitContext.additionalRepositoryCheckoutInfo.length === 0) { + if ( + !commitContext.additionalRepositoryCheckoutInfo || + commitContext.additionalRepositoryCheckoutInfo.length === 0 + ) { return commitContext.revision; } - const hasher = createHash('sha256'); + const hasher = createHash("sha256"); hasher.update(commitContext.revision); for (const info of commitContext.additionalRepositoryCheckoutInfo) { hasher.update(info.revision); } - return hasher.digest('hex'); + return hasher.digest("hex"); } - } export interface GitCheckoutInfo extends Commit { @@ -997,9 +968,7 @@ export interface GitCheckoutInfo extends Commit { export namespace CommitContext { export function is(commit: any): commit is CommitContext { - return WorkspaceContext.is(commit) - && 'repository' in commit - && 'revision' in commit + return WorkspaceContext.is(commit) && "repository" in commit && "revision" in commit; } } @@ -1007,17 +976,14 @@ export interface PullRequestContext extends CommitContext { nr: number; ref: string; base: { - repository: Repository - ref: string - } + repository: Repository; + ref: string; + }; } export namespace PullRequestContext { export function is(ctx: any): ctx is PullRequestContext { - return CommitContext.is(ctx) - && 'nr' in ctx - && 'ref' in ctx - && 'base' in ctx + return CommitContext.is(ctx) && "nr" in ctx && "ref" in ctx && "base" in ctx; } } @@ -1029,10 +995,7 @@ export interface IssueContext extends CommitContext { export namespace IssueContext { export function is(ctx: any): ctx is IssueContext { - return CommitContext.is(ctx) - && 'nr' in ctx - && 'ref' in ctx - && 'localBranch' in ctx + return CommitContext.is(ctx) && "nr" in ctx && "ref" in ctx && "localBranch" in ctx; } } @@ -1043,9 +1006,7 @@ export interface NavigatorContext extends CommitContext { export namespace NavigatorContext { export function is(ctx: any): ctx is NavigatorContext { - return CommitContext.is(ctx) - && 'path' in ctx - && 'isFile' in ctx + return CommitContext.is(ctx) && "path" in ctx && "isFile" in ctx; } } @@ -1062,8 +1023,8 @@ export interface Repository { private?: boolean; fork?: { // The direct parent of this fork - parent: Repository - } + parent: Repository; + }; } export interface Branch { name: string; @@ -1088,21 +1049,19 @@ export namespace Repository { export interface WorkspaceInstancePortsChangedEvent { type: "PortsChanged"; instanceID: string; - portsOpened: number[] - portsClosed: number[] + portsOpened: number[]; + portsClosed: number[]; } export namespace WorkspaceInstancePortsChangedEvent { - export function is(data: any): data is WorkspaceInstancePortsChangedEvent { return data && data.type == "PortsChanged"; } - } export interface WorkspaceInfo { - workspace: Workspace - latestInstance?: WorkspaceInstance + workspace: Workspace; + latestInstance?: WorkspaceInstance; } export namespace WorkspaceInfo { @@ -1118,35 +1077,36 @@ export interface WorkspaceCreationResult { workspaceURL?: string; existingWorkspaces?: WorkspaceInfo[]; runningWorkspacePrebuild?: { - prebuildID: string - workspaceID: string - instanceID: string - starting: RunningWorkspacePrebuildStarting - sameCluster: boolean - } + prebuildID: string; + workspaceID: string; + instanceID: string; + starting: RunningWorkspacePrebuildStarting; + sameCluster: boolean; + }; runningPrebuildWorkspaceID?: string; } -export type RunningWorkspacePrebuildStarting = 'queued' | 'starting' | 'running'; +export type RunningWorkspacePrebuildStarting = "queued" | "starting" | "running"; export enum CreateWorkspaceMode { // Default returns a running prebuild if there is any, otherwise creates a new workspace (using a prebuild if one is available) - Default = 'default', + Default = "default", // ForceNew creates a new workspace irrespective of any running prebuilds. This mode is guaranteed to actually create a workspace - but may degrade user experience as currently runnig prebuilds are ignored. - ForceNew = 'force-new', + ForceNew = "force-new", // UsePrebuild polls the database waiting for a currently running prebuild to become available. This mode exists to handle the db-sync delay. - UsePrebuild = 'use-prebuild', + UsePrebuild = "use-prebuild", // SelectIfRunning returns a list of currently running workspaces for the context URL if there are any, otherwise falls back to Default mode - SelectIfRunning = 'select-if-running', + SelectIfRunning = "select-if-running", } export namespace WorkspaceCreationResult { export function is(data: any): data is WorkspaceCreationResult { - return data && ( - 'createdWorkspaceId' in data - || 'existingWorkspaces' in data - || 'runningWorkspacePrebuild' in data - || 'runningPrebuildWorkspaceID' in data - ) + return ( + data && + ("createdWorkspaceId" in data || + "existingWorkspaces" in data || + "runningWorkspacePrebuild" in data || + "runningPrebuildWorkspaceID" in data) + ); } } @@ -1180,7 +1140,7 @@ export interface AuthProviderInfo { readonly default: string[]; readonly publicRepo: string[]; readonly privateRepo: string[]; - } + }; } export interface AuthProviderEntry { @@ -1206,23 +1166,27 @@ export interface OAuth2Config { readonly scopeSeparator?: string; readonly settingsUrl?: string; - readonly authorizationParams?: { [key: string]: string } + readonly authorizationParams?: { [key: string]: string }; readonly configURL?: string; } export namespace AuthProviderEntry { export type Type = "GitHub" | "GitLab" | string; export type Status = "pending" | "verified"; - export type NewEntry = Pick & { clientId?: string, clientSecret?: string }; - export type UpdateEntry = Pick & Pick; + export type NewEntry = Pick & { + clientId?: string; + clientSecret?: string; + }; + export type UpdateEntry = Pick & + Pick; export function redact(entry: AuthProviderEntry): AuthProviderEntry { return { ...entry, oauth: { ...entry.oauth, - clientSecret: "redacted" - } - } + clientSecret: "redacted", + }, + }; } } diff --git a/components/gitpod-protocol/src/snapshot-url.spec.ts b/components/gitpod-protocol/src/snapshot-url.spec.ts index 4ab617033c9044..a12b274d193062 100644 --- a/components/gitpod-protocol/src/snapshot-url.spec.ts +++ b/components/gitpod-protocol/src/snapshot-url.spec.ts @@ -4,16 +4,18 @@ * See License-AGPL.txt in the project root for license information. */ -import { suite, test } from "mocha-typescript" -import * as chai from "chai" +import { suite, test } from "mocha-typescript"; +import * as chai from "chai"; import { SnapshotUrl } from "."; -const expect = chai.expect - -@suite class TestSnapshotUrlParser { +const expect = chai.expect; +@suite +class TestSnapshotUrlParser { @test public testPositive() { - const actual = SnapshotUrl.parse("workspaces/c362d434-6faa-4ce0-9ad4-91b4a87c4abe/3f0556f7-4afa-11e9-98d5-52f8983b9279.tar@gitpod-prodcopy-user-e1e28f18-0354-4a5d-b6b4-8879a2ff73fd"); + const actual = SnapshotUrl.parse( + "workspaces/c362d434-6faa-4ce0-9ad4-91b4a87c4abe/3f0556f7-4afa-11e9-98d5-52f8983b9279.tar@gitpod-prodcopy-user-e1e28f18-0354-4a5d-b6b4-8879a2ff73fd", + ); expect(actual).to.deep.equal({ bucketId: "gitpod-prodcopy-user-e1e28f18-0354-4a5d-b6b4-8879a2ff73fd", @@ -22,4 +24,4 @@ const expect = chai.expect }); } } -module.exports = new TestSnapshotUrlParser() // Only to circumvent no usage warning :-/ +module.exports = new TestSnapshotUrlParser(); // Only to circumvent no usage warning :-/ diff --git a/components/gitpod-protocol/src/team-subscription-protocol.ts b/components/gitpod-protocol/src/team-subscription-protocol.ts index 09a290ffec6d09..8fd5c8ea4dc569 100644 --- a/components/gitpod-protocol/src/team-subscription-protocol.ts +++ b/components/gitpod-protocol/src/team-subscription-protocol.ts @@ -4,7 +4,7 @@ * See License-AGPL.txt in the project root for license information. */ -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; import { Subscription } from "./accounting-protocol"; export interface TeamSubscription { @@ -23,14 +23,14 @@ export interface TeamSubscription { } export namespace TeamSubscription { - export const create = (ts: Omit): TeamSubscription => { + export const create = (ts: Omit): TeamSubscription => { const withId = ts as TeamSubscription; withId.id = uuidv4(); return withId; - } + }; export const isActive = (ts: TeamSubscription, date: string): boolean => { return ts.startDate <= date && (ts.endDate === undefined || date < ts.endDate); - } + }; } /** @@ -44,49 +44,57 @@ export interface TeamSubscriptionSlot { subscriptionId?: string; cancellationDate?: string; } -export type TeamSubscriptionSlotDeactivated = TeamSubscriptionSlot & { assigneeId: string, assigneeIdentifier: AssigneeIdentifier }; -export type TeamSubscriptionSlotAssigned = TeamSubscriptionSlot & TeamSubscriptionSlotDeactivated & { subscriptionId: string }; +export type TeamSubscriptionSlotDeactivated = TeamSubscriptionSlot & { + assigneeId: string; + assigneeIdentifier: AssigneeIdentifier; +}; +export type TeamSubscriptionSlotAssigned = TeamSubscriptionSlot & + TeamSubscriptionSlotDeactivated & { subscriptionId: string }; -export type TeamSubscriptionSlotState = 'unassigned' | 'assigned' | 'deactivated' | 'cancelled'; +export type TeamSubscriptionSlotState = "unassigned" | "assigned" | "deactivated" | "cancelled"; export namespace TeamSubscriptionSlot { - export const create = (ts: Omit): TeamSubscriptionSlot => { + export const create = (ts: Omit): TeamSubscriptionSlot => { const withId = ts as TeamSubscriptionSlot; withId.id = uuidv4(); return withId; - } - export const assign = (slot: TeamSubscriptionSlot, assigneeId: string, subscriptionId: string, assigneeIdentifier: AssigneeIdentifier) => { + }; + export const assign = ( + slot: TeamSubscriptionSlot, + assigneeId: string, + subscriptionId: string, + assigneeIdentifier: AssigneeIdentifier, + ) => { slot.assigneeId = assigneeId; slot.subscriptionId = subscriptionId; slot.assigneeIdentifier = assigneeIdentifier; - } + }; export const deactivate = (slot: TeamSubscriptionSlot, cancellationDate: string) => { slot.subscriptionId = undefined; slot.cancellationDate = cancellationDate; - } + }; export const reactivate = (slot: TeamSubscriptionSlot, subscriptionId?: string) => { slot.subscriptionId = subscriptionId; slot.cancellationDate = undefined; - } + }; export const status = (slot: TeamSubscriptionSlot, now: string): TeamSubscriptionSlotState => { if (slot.cancellationDate) { if (slot.cancellationDate < now) { - return 'cancelled'; + return "cancelled"; } else { - return 'deactivated'; + return "deactivated"; } } else { if (slot.subscriptionId) { - return 'assigned'; + return "assigned"; } else { - return 'unassigned'; + return "unassigned"; } } - - } + }; export const isActive = (slot: TeamSubscriptionSlot): boolean => { return !slot.cancellationDate; - } + }; } /** @@ -109,7 +117,7 @@ export interface TeamSubscriptionSlotResolved { export type AssigneeIdentifier = AssigneeIdentityIdentifier; export interface AssigneeIdentityIdentifier { identity: { - authHost: string, - authName: string - } + authHost: string; + authName: string; + }; } diff --git a/components/gitpod-protocol/src/teams-projects-protocol.ts b/components/gitpod-protocol/src/teams-projects-protocol.ts index 36fd67ddecd82b..b4704ee799dffb 100644 --- a/components/gitpod-protocol/src/teams-projects-protocol.ts +++ b/components/gitpod-protocol/src/teams-projects-protocol.ts @@ -5,11 +5,11 @@ */ import { PrebuiltWorkspaceState } from "./protocol"; -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4 } from "uuid"; import { DeepPartial } from "./util/deep-partial"; export interface ProjectConfig { - '.gitpod.yml': string; + ".gitpod.yml": string; } export interface ProjectSettings { @@ -33,13 +33,13 @@ export interface Project { } export namespace Project { - export const create = (project: Omit): Project => { + export const create = (project: Omit): Project => { return { ...project, id: uuidv4(), - creationTime: new Date().toISOString() + creationTime: new Date().toISOString(), }; - } + }; export interface Overview { branches: BranchDetails[]; @@ -67,7 +67,7 @@ export namespace Project { } } -export type PartialProject = DeepPartial & Pick; +export type PartialProject = DeepPartial & Pick; export interface PrebuildWithStatus { info: PrebuildInfo; @@ -103,7 +103,7 @@ export interface PrebuildInfo { } export namespace PrebuildInfo { export function is(data?: any): data is PrebuildInfo { - return typeof data === "object" && ["id", "buildWorkspaceId", "projectId", "branch"].every(p => p in data); + return typeof data === "object" && ["id", "buildWorkspaceId", "projectId", "branch"].every((p) => p in data); } } @@ -144,4 +144,4 @@ export interface TeamMembershipInvite { /** This is a flag that triggers the HARD DELETION of this entity */ deleted?: boolean; -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/typings/globals.ts b/components/gitpod-protocol/src/typings/globals.ts index d57a80815ecd2a..47977a541ee54d 100644 --- a/components/gitpod-protocol/src/typings/globals.ts +++ b/components/gitpod-protocol/src/typings/globals.ts @@ -6,7 +6,7 @@ interface Window { gitpod: { - service: import('../gitpod-service').GitpodService - ideService?: import('../ide-frontend-service').IDEFrontendService - } -} \ No newline at end of file + service: import("../gitpod-service").GitpodService; + ideService?: import("../ide-frontend-service").IDEFrontendService; + }; +} diff --git a/components/gitpod-protocol/src/util/analytics.ts b/components/gitpod-protocol/src/util/analytics.ts index 3725c124d646e2..cb708352def9b6 100644 --- a/components/gitpod-protocol/src/util/analytics.ts +++ b/components/gitpod-protocol/src/util/analytics.ts @@ -6,8 +6,7 @@ import Analytics = require("analytics-node"); import { IAnalyticsWriter, IdentifyMessage, TrackMessage, PageMessage } from "../analytics"; -import { log } from './logging'; - +import { log } from "./logging"; export function newAnalyticsWriterFromEnv(): IAnalyticsWriter { switch (process.env.GITPOD_ANALYTICS_WRITER) { @@ -21,26 +20,28 @@ export function newAnalyticsWriterFromEnv(): IAnalyticsWriter { } class SegmentAnalyticsWriter implements IAnalyticsWriter { - protected readonly analytics: Analytics; constructor(writeKey: string) { this.analytics = new Analytics(writeKey); } - identify(msg: IdentifyMessage) { + identify(msg: IdentifyMessage) { try { - this.analytics.identify({ - ...msg, - integrations: { - "All": true, - "Mixpanel": !!msg.userId - } - }, (err: Error) => { - if (err) { - log.warn("analytics.identify failed", err); - } - }); + this.analytics.identify( + { + ...msg, + integrations: { + All: true, + Mixpanel: !!msg.userId, + }, + }, + (err: Error) => { + if (err) { + log.warn("analytics.identify failed", err); + } + }, + ); } catch (err) { log.warn("analytics.identify failed", err); } @@ -48,44 +49,48 @@ class SegmentAnalyticsWriter implements IAnalyticsWriter { track(msg: TrackMessage) { try { - this.analytics.track({ - ...msg, - integrations: { - "All": true, - "Mixpanel": !!msg.userId - } - }, (err: Error) => { - if (err) { - log.warn("analytics.track failed", err); - } - }); + this.analytics.track( + { + ...msg, + integrations: { + All: true, + Mixpanel: !!msg.userId, + }, + }, + (err: Error) => { + if (err) { + log.warn("analytics.track failed", err); + } + }, + ); } catch (err) { log.warn("analytics.track failed", err); } } page(msg: PageMessage) { - try{ - this.analytics.page({ - ...msg, - integrations: { - "All": true, - "Mixpanel": !!msg.userId - } - }, (err: Error) => { - if (err) { - log.warn("analytics.page failed", err); - } - }); + try { + this.analytics.page( + { + ...msg, + integrations: { + All: true, + Mixpanel: !!msg.userId, + }, + }, + (err: Error) => { + if (err) { + log.warn("analytics.page failed", err); + } + }, + ); } catch (err) { log.warn("analytics.page failed", err); } } - } class LogAnalyticsWriter implements IAnalyticsWriter { - identify(msg: IdentifyMessage): void { log.debug("analytics identify", msg); } @@ -95,11 +100,10 @@ class LogAnalyticsWriter implements IAnalyticsWriter { page(msg: PageMessage): void { log.debug("analytics page", msg); } - } class NoAnalyticsWriter implements IAnalyticsWriter { identify(msg: IdentifyMessage): void {} track(msg: TrackMessage): void {} page(msg: PageMessage): void {} -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/async-iterator.ts b/components/gitpod-protocol/src/util/async-iterator.ts index 190506603cb291..a5206425f755c1 100644 --- a/components/gitpod-protocol/src/util/async-iterator.ts +++ b/components/gitpod-protocol/src/util/async-iterator.ts @@ -5,8 +5,8 @@ */ // Use asyncIterators with es2015 -if (typeof (Symbol as any).asyncIterator === 'undefined') { - (Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol('asyncIterator'); +if (typeof (Symbol as any).asyncIterator === "undefined") { + (Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol("asyncIterator"); } export async function find(it: AsyncIterableIterator, predicate: (value: T) => boolean): Promise { @@ -31,12 +31,11 @@ export interface AsyncCachingIterator extends AsyncIterableIterator { resetCursor(): void; } export class AsyncCachingIteratorImpl implements AsyncIterableIterator, AsyncCachingIterator { - protected cache: T[] = []; protected cursor = 0; protected cacheRead = false; - constructor(protected readonly iterable: AsyncIterableIterator) { } + constructor(protected readonly iterable: AsyncIterableIterator) {} public resetCursor() { this.cursor = 0; @@ -47,7 +46,7 @@ export class AsyncCachingIteratorImpl implements AsyncIterableIterator, As if (!this.cacheRead && this.cursor < this.cache.length) { return { done: false, - value: this.cache[this.cursor++] + value: this.cache[this.cursor++], }; } this.cacheRead = true; diff --git a/components/gitpod-protocol/src/util/cancelable.ts b/components/gitpod-protocol/src/util/cancelable.ts index 694d705972d84a..fe3935382f4fb7 100644 --- a/components/gitpod-protocol/src/util/cancelable.ts +++ b/components/gitpod-protocol/src/util/cancelable.ts @@ -9,10 +9,10 @@ import { Disposable } from "./disposable"; export class Cancelable implements Disposable { protected canceled: boolean; - constructor(protected readonly activity: (cancel: boolean) => Promise | undefined) { } + constructor(protected readonly activity: (cancel: boolean) => Promise | undefined) {} public async run(): Promise { - for(let r = await this.activity(this.canceled); ; r = await this.activity(this.canceled)) { + for (let r = await this.activity(this.canceled); ; r = await this.activity(this.canceled)) { if (this.canceled) { return; } else if (r !== undefined) { @@ -28,4 +28,4 @@ export class Cancelable implements Disposable { dispose(): void { this.cancel(); } -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/date-time.ts b/components/gitpod-protocol/src/util/date-time.ts index f9147b47d9161c..f5f06da4376a97 100644 --- a/components/gitpod-protocol/src/util/date-time.ts +++ b/components/gitpod-protocol/src/util/date-time.ts @@ -5,26 +5,26 @@ */ export function formatDate(date?: string) { - return date ? new Date(date).toLocaleString() : ''; + return date ? new Date(date).toLocaleString() : ""; } export function formatHours(hours?: number) { if (hours === undefined) { - return ''; + return ""; } const h = Math.floor(Math.abs(hours)); - const rm = (Math.abs(hours) - h) * 60.; + const rm = (Math.abs(hours) - h) * 60; const m = Math.floor(rm); - const rs = (rm - m) * 60.; + const rs = (rm - m) * 60; const s = Math.floor(rs); - const result = h + ':' + pad2(m) + ':' + pad2(s); + const result = h + ":" + pad2(m) + ":" + pad2(s); if (hours < 0) { - return `-${result}` + return `-${result}`; } else { - return `${result}` + return `${result}`; } } function pad2(n: number) { - return n < 10 ? '0' + n : '' + n; + return n < 10 ? "0" + n : "" + n; } diff --git a/components/gitpod-protocol/src/util/deferred.ts b/components/gitpod-protocol/src/util/deferred.ts index e364fde79438ce..d47baa2d3a602f 100644 --- a/components/gitpod-protocol/src/util/deferred.ts +++ b/components/gitpod-protocol/src/util/deferred.ts @@ -19,12 +19,12 @@ export class Deferred { promise = new Promise((resolve, reject) => { this.resolve = (o) => { this.isResolved = true; - resolve(o as any) - clearTimeout(this.timer) + resolve(o as any); + clearTimeout(this.timer); }; this.reject = (e) => { - reject(e) - clearTimeout(this.timer) - } + reject(e); + clearTimeout(this.timer); + }; }); -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/disposable.ts b/components/gitpod-protocol/src/util/disposable.ts index 4c0847d1dbe3fd..53c0829007d77b 100644 --- a/components/gitpod-protocol/src/util/disposable.ts +++ b/components/gitpod-protocol/src/util/disposable.ts @@ -16,10 +16,10 @@ export interface Disposable { export namespace Disposable { export function create(func: () => void): Disposable { return { - dispose: func + dispose: func, }; } - export const NULL = create(() => { }); + export const NULL = create(() => {}); } export class DisposableCollection implements Disposable { @@ -69,9 +69,6 @@ export class DisposableCollection implements Disposable { } pushAll(disposables: Disposable[]): Disposable[] { - return disposables.map(disposable => - this.push(disposable) - ); + return disposables.map((disposable) => this.push(disposable)); } - } diff --git a/components/gitpod-protocol/src/util/event.ts b/components/gitpod-protocol/src/util/event.ts index e723a74a476792..098d39be99b6a0 100644 --- a/components/gitpod-protocol/src/util/event.ts +++ b/components/gitpod-protocol/src/util/event.ts @@ -6,13 +6,12 @@ */ import { Disposable } from "./disposable"; -import { log } from './logging'; +import { log } from "./logging"; /** * Represents a typed event. */ export interface Event { - /** * * @param listener The listener function will be call when the event happens. @@ -24,12 +23,13 @@ export interface Event { } export namespace Event { - const _disposable = { dispose() { } }; - export const None: Event = function () { return _disposable; }; + const _disposable = { dispose() {} }; + export const None: Event = function () { + return _disposable; + }; } class CallbackList { - private _callbacks: Function[] | undefined; private _contexts: any[] | undefined; @@ -66,7 +66,7 @@ class CallbackList { } if (foundCallbackWithDifferentContext) { - throw new Error('When adding a listener with a context, you should remove it with the same context'); + throw new Error("When adding a listener with a context, you should remove it with the same context"); } } @@ -105,14 +105,12 @@ export interface EmitterOptions { } export class Emitter { - - private static _noop = function () { }; + private static _noop = function () {}; private _event: Event; private _callbacks: CallbackList | undefined; - constructor(private _options?: EmitterOptions) { - } + constructor(private _options?: EmitterOptions) {} /** * For the public to allow to subscribe @@ -137,7 +135,7 @@ export class Emitter { if (this._options && this._options.onLastListenerRemove && this._callbacks!.isEmpty()) { this._options.onLastListenerRemove(this); } - } + }, }; if (Array.isArray(disposables)) { disposables.push(result); diff --git a/components/gitpod-protocol/src/util/garbage-collected-cache.ts b/components/gitpod-protocol/src/util/garbage-collected-cache.ts index 4b596ee27a6547..303759e46914f2 100644 --- a/components/gitpod-protocol/src/util/garbage-collected-cache.ts +++ b/components/gitpod-protocol/src/util/garbage-collected-cache.ts @@ -6,7 +6,6 @@ import { repeat } from "./repeat"; - interface CacheEntry { key: string; value: T; @@ -16,9 +15,7 @@ interface CacheEntry { export class GarbageCollectedCache { protected readonly store = new Map>(); - constructor( - protected readonly defaultMaxAgeSeconds: number, - protected readonly gcIntervalSeconds: number) { + constructor(protected readonly defaultMaxAgeSeconds: number, protected readonly gcIntervalSeconds: number) { this.regularlyCollectGarbage(); } @@ -61,6 +58,6 @@ export class GarbageCollectedCache { } protected calcExpiryDate(maxAgeSeconds?: number): number { - return Date.now() + ((maxAgeSeconds || this.defaultMaxAgeSeconds) * 1000); + return Date.now() + (maxAgeSeconds || this.defaultMaxAgeSeconds) * 1000; } -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/generate-workspace-id.spec.ts b/components/gitpod-protocol/src/util/generate-workspace-id.spec.ts index a5d9c06ea4edd3..4f50d8a316c71b 100644 --- a/components/gitpod-protocol/src/util/generate-workspace-id.spec.ts +++ b/components/gitpod-protocol/src/util/generate-workspace-id.spec.ts @@ -4,15 +4,15 @@ * See License-AGPL.txt in the project root for license information. */ -import { suite, test } from "mocha-typescript" -import * as chai from "chai" +import { suite, test } from "mocha-typescript"; +import * as chai from "chai"; import { generateWorkspaceID, colors, animals } from "./generate-workspace-id"; import { GitpodHostUrl } from "./gitpod-host-url"; -const expect = chai.expect - -@suite class TestGenerateWorkspaceId { +const expect = chai.expect; +@suite +class TestGenerateWorkspaceId { @test public async testGenerateWorkspaceId() { for (let i = 0; i < 100; i++) { const id = await generateWorkspaceID(); @@ -29,20 +29,23 @@ const expect = chai.expect @test public async testCustomName() { const data = [ - ['foo','bar','foo-bar-'], - ['f','bar','.{2,16}-bar-'], - ['gitpod-io','gitpod','gitpodio-gitpod-'], - ['this is rather long and has some "§$"% special chars','also here pretty long and needs abbreviation','thisisratherlon-alsohere-'], - ['breatheco-de', 'python-flask-api-tutorial', 'breathecode-pythonflaska-'], - ['UPPER', "CaSe", "upper-case-"] - ] + ["foo", "bar", "foo-bar-"], + ["f", "bar", ".{2,16}-bar-"], + ["gitpod-io", "gitpod", "gitpodio-gitpod-"], + [ + 'this is rather long and has some "§$"% special chars', + "also here pretty long and needs abbreviation", + "thisisratherlon-alsohere-", + ], + ["breatheco-de", "python-flask-api-tutorial", "breathecode-pythonflaska-"], + ["UPPER", "CaSe", "upper-case-"], + ]; for (const d of data) { const id = await generateWorkspaceID(d[0], d[1]); - expect(id).match(new RegExp("^"+d[2])); + expect(id).match(new RegExp("^" + d[2])); expect(new GitpodHostUrl().withWorkspacePrefix(id, "eu").workspaceId).to.equal(id); expect(id.length <= 36, `"${id}" is longer than 36 chars (${id.length})`).to.be.true; } } - } -module.exports = new TestGenerateWorkspaceId() +module.exports = new TestGenerateWorkspaceId(); diff --git a/components/gitpod-protocol/src/util/generate-workspace-id.ts b/components/gitpod-protocol/src/util/generate-workspace-id.ts index f70199537cfc0a..97f1fd7f545376 100644 --- a/components/gitpod-protocol/src/util/generate-workspace-id.ts +++ b/components/gitpod-protocol/src/util/generate-workspace-id.ts @@ -6,470 +6,470 @@ import randomNumber = require("random-number-csprng"); export async function generateWorkspaceID(firstSegment?: string, secondSegment?: string): Promise { - const firstSeg = clean(firstSegment) || await random(colors); - const secSeg = clean(secondSegment, Math.min(15, 23 - firstSeg.length)) || await random(animals); - return firstSeg+'-'+secSeg+'-'+(await random(characters, 11)); + const firstSeg = clean(firstSegment) || (await random(colors)); + const secSeg = clean(secondSegment, Math.min(15, 23 - firstSeg.length)) || (await random(animals)); + return firstSeg + "-" + secSeg + "-" + (await random(characters, 11)); } function clean(segment: string | undefined, maxChars: number = 15) { - if (!segment) { - return undefined; - } - segment = segment.toLowerCase(); - let result = ''; - for (let i =0; i < segment.length; i++) { - if (characters.indexOf(segment[i]) !== -1) { - result += segment[i]; + if (!segment) { + return undefined; + } + segment = segment.toLowerCase(); + let result = ""; + for (let i = 0; i < segment.length; i++) { + if (characters.indexOf(segment[i]) !== -1) { + result += segment[i]; + } + } + if (result.length >= 2) { + return result.substring(0, maxChars); } - } - if (result.length >= 2) { - return result.substring(0, maxChars); - } } async function random(array: string[], length: number = 1): Promise { - var result = ''; - for ( var i = 0; i < length; i++ ) { - result += array[await randomNumber(0, array.length-1)]; + var result = ""; + for (var i = 0; i < length; i++) { + result += array[await randomNumber(0, array.length - 1)]; } return result; } -const characters = 'abcdefghijklmnopqrstuvwxyz0123456789'.split(''); +const characters = "abcdefghijklmnopqrstuvwxyz0123456789".split(""); export const colors = [ - 'amaranth', - 'amber', - 'amethyst', - 'apricot', - 'aqua', - 'aquamarine', - 'azure', - 'beige', - 'black', - 'blue', - 'blush', - 'bronze', - 'brown', - 'chocolate', - 'coffee', - 'copper', - 'coral', - 'crimson', - 'cyan', - 'emerald', - 'fuchsia', - 'gold', - 'gray', - 'green', - 'harlequin', - 'indigo', - 'ivory', - 'jade', - 'kumquat', - 'lavender', - 'lime', - 'magenta', - 'maroon', - 'moccasin', - 'olive', - 'orange', - 'peach', - 'pink', - 'plum', - 'purple', - 'red', - 'rose', - 'salmon', - 'sapphire', - 'scarlet', - 'silver', - 'tan', - 'teal', - 'tomato', - 'turquoise', - 'violet', - 'white', - 'yellow', - ]; + "amaranth", + "amber", + "amethyst", + "apricot", + "aqua", + "aquamarine", + "azure", + "beige", + "black", + "blue", + "blush", + "bronze", + "brown", + "chocolate", + "coffee", + "copper", + "coral", + "crimson", + "cyan", + "emerald", + "fuchsia", + "gold", + "gray", + "green", + "harlequin", + "indigo", + "ivory", + "jade", + "kumquat", + "lavender", + "lime", + "magenta", + "maroon", + "moccasin", + "olive", + "orange", + "peach", + "pink", + "plum", + "purple", + "red", + "rose", + "salmon", + "sapphire", + "scarlet", + "silver", + "tan", + "teal", + "tomato", + "turquoise", + "violet", + "white", + "yellow", +]; - export const animals = [ - 'canidae', - 'felidae', - 'cat', - 'cattle', - 'dog', - 'donkey', - 'goat', - 'horse', - 'pig', - 'rabbit', - 'aardvark', - 'aardwolf', - 'albatross', - 'alligator', - 'alpaca', - 'amphibian', - 'anaconda', - 'angelfish', - 'anglerfish', - 'ant', - 'anteater', - 'antelope', - 'antlion', - 'ape', - 'aphid', - 'armadillo', - 'asp', - 'baboon', - 'badger', - 'bandicoot', - 'barnacle', - 'barracuda', - 'basilisk', - 'bass', - 'bat', - 'bear', - 'beaver', - 'bedbug', - 'bee', - 'beetle', - 'bird', - 'bison', - 'blackbird', - 'boa', - 'boar', - 'bobcat', - 'bobolink', - 'bonobo', - 'booby', - 'bovid', - 'bug', - 'butterfly', - 'buzzard', - 'camel', - 'canid', - 'capybara', - 'cardinal', - 'caribou', - 'carp', - 'cat', - 'catshark', - 'caterpillar', - 'catfish', - 'cattle', - 'centipede', - 'cephalopod', - 'chameleon', - 'cheetah', - 'chickadee', - 'chicken', - 'chimpanzee', - 'chinchilla', - 'chipmunk', - 'clam', - 'clownfish', - 'cobra', - 'cockroach', - 'cod', - 'condor', - 'constrictor', - 'coral', - 'cougar', - 'cow', - 'coyote', - 'crab', - 'crane', - 'crawdad', - 'crayfish', - 'cricket', - 'crocodile', - 'crow', - 'cuckoo', - 'cicada', - 'damselfly', - 'deer', - 'dingo', - 'dinosaur', - 'dodo', - 'dog', - 'dolphin', - 'donkey', - 'dormouse', - 'dove', - 'dragonfly', - 'dragon', - 'duck', - 'eagle', - 'earthworm', - 'earwig', - 'echidna', - 'eel', - 'egret', - 'elephant', - 'elk', - 'emu', - 'ermine', - 'falcon', - 'ferret', - 'finch', - 'firefly', - 'fish', - 'flamingo', - 'flea', - 'fly', - 'flyingfish', - 'fowl', - 'fox', - 'frog', - 'gamefowl', - 'galliform', - 'gazelle', - 'gecko', - 'gerbil', - 'gibbon', - 'giraffe', - 'goat', - 'goldfish', - 'goose', - 'gopher', - 'gorilla', - 'grasshopper', - 'grouse', - 'guan', - 'guanaco', - 'guineafowl', - 'gull', - 'guppy', - 'haddock', - 'halibut', - 'hamster', - 'hare', - 'harrier', - 'hawk', - 'hedgehog', - 'heron', - 'herring', - 'hippopotamus', - 'hookworm', - 'hornet', - 'horse', - 'hoverfly', - 'hummingbird', - 'hyena', - 'iguana', - 'impala', - 'jackal', - 'jaguar', - 'jay', - 'jellyfish', - 'junglefowl', - 'kangaroo', - 'kingfisher', - 'kite', - 'kiwi', - 'koala', - 'koi', - 'krill', - 'ladybug', - 'lamprey', - 'landfowl', - 'lark', - 'leech', - 'lemming', - 'lemur', - 'leopard', - 'leopon', - 'limpet', - 'lion', - 'lizard', - 'llama', - 'lobster', - 'locust', - 'loon', - 'louse', - 'lungfish', - 'lynx', - 'macaw', - 'mackerel', - 'magpie', - 'mammal', - 'manatee', - 'mandrill', - 'marlin', - 'marmoset', - 'marmot', - 'marsupial', - 'marten', - 'mastodon', - 'meadowlark', - 'meerkat', - 'mink', - 'minnow', - 'mite', - 'mockingbird', - 'mole', - 'mollusk', - 'mongoose', - 'monkey', - 'moose', - 'mosquito', - 'moth', - 'mouse', - 'mule', - 'muskox', - 'narwhal', - 'newt', - 'nightingale', - 'ocelot', - 'octopus', - 'opossum', - 'orangutan', - 'orca', - 'ostrich', - 'otter', - 'owl', - 'ox', - 'panda', - 'panther', - 'parakeet', - 'parrot', - 'parrotfish', - 'partridge', - 'peacock', - 'peafowl', - 'pelican', - 'penguin', - 'perch', - 'pheasant', - 'pig', - 'pigeon', - 'pike', - 'pinniped', - 'piranha', - 'planarian', - 'platypus', - 'pony', - 'porcupine', - 'porpoise', - 'possum', - 'prawn', - 'primate', - 'ptarmigan', - 'puffin', - 'puma', - 'python', - 'quail', - 'quelea', - 'quokka', - 'rabbit', - 'raccoon', - 'rat', - 'rattlesnake', - 'raven', - 'reindeer', - 'reptile', - 'rhinoceros', - 'roadrunner', - 'rodent', - 'rook', - 'rooster', - 'roundworm', - 'sailfish', - 'salamander', - 'salmon', - 'sawfish', - 'scallop', - 'scorpion', - 'seahorse', - 'shark', - 'sheep', - 'shrew', - 'shrimp', - 'silkworm', - 'silverfish', - 'skink', - 'skunk', - 'sloth', - 'slug', - 'smelt', - 'snail', - 'snake', - 'snipe', - 'sole', - 'sparrow', - 'spider', - 'spoonbill', - 'squid', - 'squirrel', - 'starfish', - 'stingray', - 'stoat', - 'stork', - 'sturgeon', - 'swallow', - 'swan', - 'swift', - 'swordfish', - 'swordtail', - 'tahr', - 'takin', - 'tapir', - 'tarantula', - 'tarsier', - 'termite', - 'tern', - 'thrush', - 'tick', - 'tiger', - 'tiglon', - 'toad', - 'tortoise', - 'toucan', - 'trout', - 'tuna', - 'turkey', - 'turtle', - 'tyrannosaurus', - 'urial', - 'vicuna', - 'viper', - 'vole', - 'vulture', - 'wallaby', - 'walrus', - 'wasp', - 'warbler', - 'weasel', - 'whale', - 'whippet', - 'whitefish', - 'wildcat', - 'wildebeest', - 'wildfowl', - 'wolf', - 'wolverine', - 'wombat', - 'woodpecker', - 'worm', - 'wren', - 'xerinae', - 'yak', - 'zebra', - 'alpaca', - 'cat', - 'cattle', - 'chicken', - 'dog', - 'donkey', - 'ferret', - 'gayal', - 'goldfish', - 'guppy', - 'horse', - 'koi', - 'llama', - 'sheep', - 'yak', - 'unicorn', - ] \ No newline at end of file +export const animals = [ + "canidae", + "felidae", + "cat", + "cattle", + "dog", + "donkey", + "goat", + "horse", + "pig", + "rabbit", + "aardvark", + "aardwolf", + "albatross", + "alligator", + "alpaca", + "amphibian", + "anaconda", + "angelfish", + "anglerfish", + "ant", + "anteater", + "antelope", + "antlion", + "ape", + "aphid", + "armadillo", + "asp", + "baboon", + "badger", + "bandicoot", + "barnacle", + "barracuda", + "basilisk", + "bass", + "bat", + "bear", + "beaver", + "bedbug", + "bee", + "beetle", + "bird", + "bison", + "blackbird", + "boa", + "boar", + "bobcat", + "bobolink", + "bonobo", + "booby", + "bovid", + "bug", + "butterfly", + "buzzard", + "camel", + "canid", + "capybara", + "cardinal", + "caribou", + "carp", + "cat", + "catshark", + "caterpillar", + "catfish", + "cattle", + "centipede", + "cephalopod", + "chameleon", + "cheetah", + "chickadee", + "chicken", + "chimpanzee", + "chinchilla", + "chipmunk", + "clam", + "clownfish", + "cobra", + "cockroach", + "cod", + "condor", + "constrictor", + "coral", + "cougar", + "cow", + "coyote", + "crab", + "crane", + "crawdad", + "crayfish", + "cricket", + "crocodile", + "crow", + "cuckoo", + "cicada", + "damselfly", + "deer", + "dingo", + "dinosaur", + "dodo", + "dog", + "dolphin", + "donkey", + "dormouse", + "dove", + "dragonfly", + "dragon", + "duck", + "eagle", + "earthworm", + "earwig", + "echidna", + "eel", + "egret", + "elephant", + "elk", + "emu", + "ermine", + "falcon", + "ferret", + "finch", + "firefly", + "fish", + "flamingo", + "flea", + "fly", + "flyingfish", + "fowl", + "fox", + "frog", + "gamefowl", + "galliform", + "gazelle", + "gecko", + "gerbil", + "gibbon", + "giraffe", + "goat", + "goldfish", + "goose", + "gopher", + "gorilla", + "grasshopper", + "grouse", + "guan", + "guanaco", + "guineafowl", + "gull", + "guppy", + "haddock", + "halibut", + "hamster", + "hare", + "harrier", + "hawk", + "hedgehog", + "heron", + "herring", + "hippopotamus", + "hookworm", + "hornet", + "horse", + "hoverfly", + "hummingbird", + "hyena", + "iguana", + "impala", + "jackal", + "jaguar", + "jay", + "jellyfish", + "junglefowl", + "kangaroo", + "kingfisher", + "kite", + "kiwi", + "koala", + "koi", + "krill", + "ladybug", + "lamprey", + "landfowl", + "lark", + "leech", + "lemming", + "lemur", + "leopard", + "leopon", + "limpet", + "lion", + "lizard", + "llama", + "lobster", + "locust", + "loon", + "louse", + "lungfish", + "lynx", + "macaw", + "mackerel", + "magpie", + "mammal", + "manatee", + "mandrill", + "marlin", + "marmoset", + "marmot", + "marsupial", + "marten", + "mastodon", + "meadowlark", + "meerkat", + "mink", + "minnow", + "mite", + "mockingbird", + "mole", + "mollusk", + "mongoose", + "monkey", + "moose", + "mosquito", + "moth", + "mouse", + "mule", + "muskox", + "narwhal", + "newt", + "nightingale", + "ocelot", + "octopus", + "opossum", + "orangutan", + "orca", + "ostrich", + "otter", + "owl", + "ox", + "panda", + "panther", + "parakeet", + "parrot", + "parrotfish", + "partridge", + "peacock", + "peafowl", + "pelican", + "penguin", + "perch", + "pheasant", + "pig", + "pigeon", + "pike", + "pinniped", + "piranha", + "planarian", + "platypus", + "pony", + "porcupine", + "porpoise", + "possum", + "prawn", + "primate", + "ptarmigan", + "puffin", + "puma", + "python", + "quail", + "quelea", + "quokka", + "rabbit", + "raccoon", + "rat", + "rattlesnake", + "raven", + "reindeer", + "reptile", + "rhinoceros", + "roadrunner", + "rodent", + "rook", + "rooster", + "roundworm", + "sailfish", + "salamander", + "salmon", + "sawfish", + "scallop", + "scorpion", + "seahorse", + "shark", + "sheep", + "shrew", + "shrimp", + "silkworm", + "silverfish", + "skink", + "skunk", + "sloth", + "slug", + "smelt", + "snail", + "snake", + "snipe", + "sole", + "sparrow", + "spider", + "spoonbill", + "squid", + "squirrel", + "starfish", + "stingray", + "stoat", + "stork", + "sturgeon", + "swallow", + "swan", + "swift", + "swordfish", + "swordtail", + "tahr", + "takin", + "tapir", + "tarantula", + "tarsier", + "termite", + "tern", + "thrush", + "tick", + "tiger", + "tiglon", + "toad", + "tortoise", + "toucan", + "trout", + "tuna", + "turkey", + "turtle", + "tyrannosaurus", + "urial", + "vicuna", + "viper", + "vole", + "vulture", + "wallaby", + "walrus", + "wasp", + "warbler", + "weasel", + "whale", + "whippet", + "whitefish", + "wildcat", + "wildebeest", + "wildfowl", + "wolf", + "wolverine", + "wombat", + "woodpecker", + "worm", + "wren", + "xerinae", + "yak", + "zebra", + "alpaca", + "cat", + "cattle", + "chicken", + "dog", + "donkey", + "ferret", + "gayal", + "goldfish", + "guppy", + "horse", + "koi", + "llama", + "sheep", + "yak", + "unicorn", +]; diff --git a/components/gitpod-protocol/src/util/gitpod-cookie.ts b/components/gitpod-protocol/src/util/gitpod-cookie.ts index f581c1d934dc64..b32c4596d38281 100644 --- a/components/gitpod-protocol/src/util/gitpod-cookie.ts +++ b/components/gitpod-protocol/src/util/gitpod-cookie.ts @@ -3,8 +3,7 @@ * Licensed under the GNU Affero General Public License (AGPL). * See License-AGPL.txt in the project root for license information. */ -import * as cookie from 'cookie'; - +import * as cookie from "cookie"; /** * This cookie indicates whether the connected client is a Gitpod user (= "has logged in within the last year") or not. @@ -20,20 +19,20 @@ export const VALUE = "true"; export function options(domain: string): cookie.CookieSerializeOptions { // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies return { - path: "/", // make sure we send the cookie to all sub-pages + path: "/", // make sure we send the cookie to all sub-pages httpOnly: false, secure: false, - maxAge: 60 * 60 * 24 * 365, // 1 year - sameSite: "lax", // default: true. "Lax" needed to ensure we see cookies from users that neavigate to gitpod.io from external sites - domain: `.${domain}`, // explicilty include subdomains to not only cover "gitpod.io", but also "www.gitpod.io" or workspaces + maxAge: 60 * 60 * 24 * 365, // 1 year + sameSite: "lax", // default: true. "Lax" needed to ensure we see cookies from users that neavigate to gitpod.io from external sites + domain: `.${domain}`, // explicilty include subdomains to not only cover "gitpod.io", but also "www.gitpod.io" or workspaces }; -}; +} export function generateCookie(domain: string): string { return cookie.serialize(NAME, VALUE, options(domain)); -}; +} export function isPresent(cookies: string): boolean { // needs to match the old (gitpod-user=loggedIn) and new (gitpod-user=true) values to ensure a smooth transition during rollout. return !!cookies.match(`${NAME}=`); -}; \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/gitpod-host-url.spec.ts b/components/gitpod-protocol/src/util/gitpod-host-url.spec.ts index 3ae4736fb4486c..ad96a01e418c13 100644 --- a/components/gitpod-protocol/src/util/gitpod-host-url.spec.ts +++ b/components/gitpod-protocol/src/util/gitpod-host-url.spec.ts @@ -4,46 +4,73 @@ * See License-AGPL.txt in the project root for license information. */ -import * as chai from 'chai'; -import { suite, test } from 'mocha-typescript'; -import { GitpodHostUrl } from './gitpod-host-url'; +import * as chai from "chai"; +import { suite, test } from "mocha-typescript"; +import { GitpodHostUrl } from "./gitpod-host-url"; const expect = chai.expect; @suite export class GitpodHostUrlTest { - @test public parseWorkspaceId_pathBased() { - const actual = GitpodHostUrl.fromWorkspaceUrl("http://35.223.201.195/workspace/bc77e03d-c781-4235-bca0-e24087f5e472/").workspaceId; + const actual = GitpodHostUrl.fromWorkspaceUrl( + "http://35.223.201.195/workspace/bc77e03d-c781-4235-bca0-e24087f5e472/", + ).workspaceId; expect(actual).to.equal("bc77e03d-c781-4235-bca0-e24087f5e472"); } @test public parseWorkspaceId_hosts_withEnvVarsInjected() { - const actual = GitpodHostUrl.fromWorkspaceUrl("https://gray-grasshopper-nfbitfia.ws-eu02.gitpod-staging.com/#passedin=test%20value/https://github.com/gitpod-io/gitpod-test-repo").workspaceId; + const actual = GitpodHostUrl.fromWorkspaceUrl( + "https://gray-grasshopper-nfbitfia.ws-eu02.gitpod-staging.com/#passedin=test%20value/https://github.com/gitpod-io/gitpod-test-repo", + ).workspaceId; expect(actual).to.equal("gray-grasshopper-nfbitfia"); } @test public async testWithoutWorkspacePrefix() { - expect(GitpodHostUrl.fromWorkspaceUrl("https://3000-moccasin-ferret-155799b3.ws-eu02.gitpod-staging.com/").withoutWorkspacePrefix().toString()).to.equal("https://gitpod-staging.com/"); + expect( + GitpodHostUrl.fromWorkspaceUrl("https://3000-moccasin-ferret-155799b3.ws-eu02.gitpod-staging.com/") + .withoutWorkspacePrefix() + .toString(), + ).to.equal("https://gitpod-staging.com/"); } @test public async testWithoutWorkspacePrefix2() { - expect(GitpodHostUrl.fromWorkspaceUrl("https://gitpod-staging.com/").withoutWorkspacePrefix().toString()).to.equal("https://gitpod-staging.com/"); + expect( + GitpodHostUrl.fromWorkspaceUrl("https://gitpod-staging.com/").withoutWorkspacePrefix().toString(), + ).to.equal("https://gitpod-staging.com/"); } @test public async testWithoutWorkspacePrefix3() { - expect(GitpodHostUrl.fromWorkspaceUrl("https://gray-rook-5523v5d8.ws-dev.my-branch-1234.staging.gitpod-dev.com/").withoutWorkspacePrefix().toString()).to.equal("https://my-branch-1234.staging.gitpod-dev.com/"); + expect( + GitpodHostUrl.fromWorkspaceUrl("https://gray-rook-5523v5d8.ws-dev.my-branch-1234.staging.gitpod-dev.com/") + .withoutWorkspacePrefix() + .toString(), + ).to.equal("https://my-branch-1234.staging.gitpod-dev.com/"); } @test public async testWithoutWorkspacePrefix4() { - expect(GitpodHostUrl.fromWorkspaceUrl("https://my-branch-1234.staging.gitpod-dev.com/").withoutWorkspacePrefix().toString()).to.equal("https://my-branch-1234.staging.gitpod-dev.com/"); + expect( + GitpodHostUrl.fromWorkspaceUrl("https://my-branch-1234.staging.gitpod-dev.com/") + .withoutWorkspacePrefix() + .toString(), + ).to.equal("https://my-branch-1234.staging.gitpod-dev.com/"); } @test public async testWithoutWorkspacePrefix5() { - expect(GitpodHostUrl.fromWorkspaceUrl("https://abc-nice-brunch-4224.staging.gitpod-dev.com/").withoutWorkspacePrefix().toString()).to.equal("https://abc-nice-brunch-4224.staging.gitpod-dev.com/"); + expect( + GitpodHostUrl.fromWorkspaceUrl("https://abc-nice-brunch-4224.staging.gitpod-dev.com/") + .withoutWorkspacePrefix() + .toString(), + ).to.equal("https://abc-nice-brunch-4224.staging.gitpod-dev.com/"); } @test public async testWithoutWorkspacePrefix6() { - expect(GitpodHostUrl.fromWorkspaceUrl("https://gray-rook-5523v5d8.ws-dev.abc-nice-brunch-4224.staging.gitpod-dev.com/").withoutWorkspacePrefix().toString()).to.equal("https://abc-nice-brunch-4224.staging.gitpod-dev.com/"); + expect( + GitpodHostUrl.fromWorkspaceUrl( + "https://gray-rook-5523v5d8.ws-dev.abc-nice-brunch-4224.staging.gitpod-dev.com/", + ) + .withoutWorkspacePrefix() + .toString(), + ).to.equal("https://abc-nice-brunch-4224.staging.gitpod-dev.com/"); } } -module.exports = new GitpodHostUrlTest() \ No newline at end of file +module.exports = new GitpodHostUrlTest(); diff --git a/components/gitpod-protocol/src/util/gitpod-host-url.ts b/components/gitpod-protocol/src/util/gitpod-host-url.ts index fbd61fcb3a9be9..281ed4acdbf6a0 100644 --- a/components/gitpod-protocol/src/util/gitpod-host-url.ts +++ b/components/gitpod-protocol/src/util/gitpod-host-url.ts @@ -4,15 +4,16 @@ * See License-AGPL.txt in the project root for license information. */ -const URL = require('url').URL || window.URL; -import { log } from './logging'; +const URL = require("url").URL || window.URL; +import { log } from "./logging"; export interface UrlChange { - (old: URL): Partial + (old: URL): Partial; } export type UrlUpdate = UrlChange | Partial; -const baseWorkspaceIDRegex = "(([a-f][0-9a-f]{7}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|([0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8,11}))"; +const baseWorkspaceIDRegex = + "(([a-f][0-9a-f]{7}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|([0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8,11}))"; // this pattern matches v4 UUIDs as well as the new generated workspace ids (e.g. pink-panda-ns35kd21) const workspaceIDRegex = RegExp(`^${baseWorkspaceIDRegex}$`); @@ -24,15 +25,15 @@ export class GitpodHostUrl { readonly url: URL; constructor(urlParam?: string | URL) { - if (urlParam === undefined || typeof urlParam === 'string') { - this.url = new URL(urlParam || 'https://gitpod.io'); - this.url.search = ''; - this.url.hash = ''; - this.url.pathname = ''; + if (urlParam === undefined || typeof urlParam === "string") { + this.url = new URL(urlParam || "https://gitpod.io"); + this.url.search = ""; + this.url.hash = ""; + this.url.pathname = ""; } else if (urlParam instanceof URL) { this.url = urlParam; } else { - log.error('Unexpected urlParam', { urlParam }); + log.error("Unexpected urlParam", { urlParam }); } } @@ -45,7 +46,7 @@ export class GitpodHostUrl { } withDomainPrefix(prefix: string): GitpodHostUrl { - return this.with(url => ({ host: prefix + url.host }));; + return this.with((url) => ({ host: prefix + url.host })); } withoutWorkspacePrefix(): GitpodHostUrl { @@ -58,14 +59,14 @@ export class GitpodHostUrl { } withoutDomainPrefix(removeSegmentsCount: number): GitpodHostUrl { - return this.with(url => ({ host: url.host.split('.').splice(removeSegmentsCount).join('.') })); + return this.with((url) => ({ host: url.host.split(".").splice(removeSegmentsCount).join(".") })); } with(urlUpdate: UrlUpdate) { - const update = typeof urlUpdate === 'function' ? urlUpdate(this.url) : urlUpdate; - const addSlashToPath = update.pathname && update.pathname.length > 0 && !update.pathname.startsWith('/'); + const update = typeof urlUpdate === "function" ? urlUpdate(this.url) : urlUpdate; + const addSlashToPath = update.pathname && update.pathname.length > 0 && !update.pathname.startsWith("/"); if (addSlashToPath) { - update.pathname = '/' + update.pathname; + update.pathname = "/" + update.pathname; } const result = Object.assign(new URL(this.toString()), update); return new GitpodHostUrl(result); @@ -73,50 +74,53 @@ export class GitpodHostUrl { withApi(urlUpdate?: UrlUpdate) { const updated = urlUpdate ? this.with(urlUpdate) : this; - return updated.with(url => ({ pathname: `/api${url.pathname}` })); + return updated.with((url) => ({ pathname: `/api${url.pathname}` })); } withContext(contextUrl: string) { - return this.with(url => ({ hash: contextUrl })); + return this.with((url) => ({ hash: contextUrl })); } asWebsocket(): GitpodHostUrl { - return this.with(url => ({ protocol: url.protocol === 'https:' ? 'wss:' : 'ws:' })); + return this.with((url) => ({ protocol: url.protocol === "https:" ? "wss:" : "ws:" })); } asDashboard(): GitpodHostUrl { - return this.with(url => ({ pathname: '/' })); + return this.with((url) => ({ pathname: "/" })); } asLogin(): GitpodHostUrl { - return this.with(url => ({ pathname: '/login' })); + return this.with((url) => ({ pathname: "/login" })); } asUpgradeSubscription(): GitpodHostUrl { - return this.with(url => ({ pathname: '/plans' })); + return this.with((url) => ({ pathname: "/plans" })); } asAccessControl(): GitpodHostUrl { - return this.with(url => ({ pathname: '/integrations' })); + return this.with((url) => ({ pathname: "/integrations" })); } asSettings(): GitpodHostUrl { - return this.with(url => ({ pathname: '/settings' })); + return this.with((url) => ({ pathname: "/settings" })); } asPreferences(): GitpodHostUrl { - return this.with(url => ({ pathname: '/preferences' })); + return this.with((url) => ({ pathname: "/preferences" })); } asStart(workspaceId = this.workspaceId): GitpodHostUrl { return this.withoutWorkspacePrefix().with({ - pathname: '/start/', - hash: '#' + workspaceId + pathname: "/start/", + hash: "#" + workspaceId, }); } asWorkspaceAuth(instanceID: string, redirect?: boolean): GitpodHostUrl { - return this.with(url => ({ pathname: `/api/auth/workspace-cookie/${instanceID}`, search: redirect ? "redirect" : "" })); + return this.with((url) => ({ + pathname: `/api/auth/workspace-cookie/${instanceID}`, + search: redirect ? "redirect" : "", + })); } toString() { @@ -125,7 +129,7 @@ export class GitpodHostUrl { toStringWoRootSlash() { let result = this.toString(); - if (result.endsWith('/')) { + if (result.endsWith("/")) { result = result.slice(0, result.length - 1); } return result; @@ -142,7 +146,7 @@ export class GitpodHostUrl { } } - const pathSegs = this.url.pathname.split("/") + const pathSegs = this.url.pathname.split("/"); if (pathSegs.length > 3 && pathSegs[1] === "workspace") { return pathSegs[2]; } @@ -152,20 +156,19 @@ export class GitpodHostUrl { get blobServe(): boolean { const hostSegments = this.url.host.split("."); - if (hostSegments[0] === 'blobserve') { + if (hostSegments[0] === "blobserve") { return true; } - const pathSegments = this.url.pathname.split("/") + const pathSegments = this.url.pathname.split("/"); return pathSegments[0] === "blobserve"; } asSorry(message: string) { - return this.with({ pathname: '/sorry', hash: message }); + return this.with({ pathname: "/sorry", hash: message }); } asApiLogout(): GitpodHostUrl { - return this.withApi(url => ({ pathname: '/logout/' })); + return this.withApi((url) => ({ pathname: "/logout/" })); } - } diff --git a/components/gitpod-protocol/src/util/grpc.ts b/components/gitpod-protocol/src/util/grpc.ts index 1d0f54cca63ed7..f3c9fc920c1239 100644 --- a/components/gitpod-protocol/src/util/grpc.ts +++ b/components/gitpod-protocol/src/util/grpc.ts @@ -4,7 +4,7 @@ * See License-AGPL.txt in the project root for license information. */ - export const defaultGRPCOptions = { +export const defaultGRPCOptions = { "grpc.keepalive_timeout_ms": 10000, "grpc.keepalive_time_ms": 60000, "grpc.http2.min_time_between_pings_ms": 10000, diff --git a/components/gitpod-protocol/src/util/jaeger-client-types.ts b/components/gitpod-protocol/src/util/jaeger-client-types.ts index 022d024cfa17e0..91bc278b60fd6f 100644 --- a/components/gitpod-protocol/src/util/jaeger-client-types.ts +++ b/components/gitpod-protocol/src/util/jaeger-client-types.ts @@ -93,10 +93,10 @@ export interface SamplingDecision { // added by TypeFox export interface Sampler { - name(): string + name(): string; isSampled(operation: string, tags: any): boolean; onCreateSpan(span: opentracing.Span): SamplingDecision; onSetOperationName(span: opentracing.Span, operationName: string): SamplingDecision; onSetTag(span: opentracing.Span, key: string, value: any): SamplingDecision; - close(callback: () => void): void + close(callback: () => void): void; } diff --git a/components/gitpod-protocol/src/util/logging.ts b/components/gitpod-protocol/src/util/logging.ts index d04ae3a1194864..cb9aaf8c1d810f 100644 --- a/components/gitpod-protocol/src/util/logging.ts +++ b/components/gitpod-protocol/src/util/logging.ts @@ -4,7 +4,7 @@ * See License-AGPL.txt in the project root for license information. */ -const inspect: (object: any) => string = require('util').inspect; // undefined in frontend +const inspect: (object: any) => string = require("util").inspect; // undefined in frontend let jsonLogging: boolean = false; let component: string | undefined; @@ -15,19 +15,19 @@ export interface LogContext { sessionId?: string; userId?: string; workspaceId?: string; -}; +} export namespace LogContext { - export function from(params : { userId?: string, user?: any, request?: any } ) { + export function from(params: { userId?: string; user?: any; request?: any }) { return { sessionId: params.request?.requestID, - userId: params.userId || params.user?.id - } + userId: params.userId || params.user?.id, + }; } } export interface LogPayload { // placeholder to indicate that only dictionary-style objects should be passed as payload -}; +} export namespace log { export function error(context: LogContext, message: string, error: any, payload: LogPayload): void; @@ -97,7 +97,11 @@ export namespace log { /** * Do not use in frontend. */ - export function enableJSONLogging(componentArg: string, versionArg: string | undefined, logLevel?: LogrusLogLevel): void { + export function enableJSONLogging( + componentArg: string, + versionArg: string | undefined, + logLevel?: LogrusLogLevel, + ): void { component = componentArg; version = versionArg; @@ -109,16 +113,16 @@ export namespace log { console.error = function (...args: any[]): void { errorLog(true, args); - } + }; console.warn = function (...args: any[]): void { warnLog(true, args); - } + }; console.info = function (...args: any[]): void { infoLog(true, args); - } + }; console.debug = function (...args: any[]): void { debugLog(true, args); - } + }; console.log = console.info; // FIXME wrap also other console methods (e.g. trace()) @@ -154,26 +158,26 @@ type DoLogFunction = (calledViaConsole: boolean, args: any[]) => void; let errorLog = doErrorLog; function doErrorLog(calledViaConsole: boolean, args: any[]): void { - doLog(calledViaConsole, errorConsoleLog, 'ERROR', args); + doLog(calledViaConsole, errorConsoleLog, "ERROR", args); } let warnLog = doWarnLog; function doWarnLog(calledViaConsole: boolean, args: any[]): void { - doLog(calledViaConsole, warnConsoleLog, 'WARNING', args); + doLog(calledViaConsole, warnConsoleLog, "WARNING", args); } let infoLog = doInfoLog; function doInfoLog(calledViaConsole: boolean, args: any[]): void { - doLog(calledViaConsole, infoConsoleLog, 'INFO', args); + doLog(calledViaConsole, infoConsoleLog, "INFO", args); } let debugLog = doDebugLog; function doDebugLog(calledViaConsole: boolean, args: any[]): void { - doLog(calledViaConsole, debugConsoleLog, 'DEBUG', args); + doLog(calledViaConsole, debugConsoleLog, "DEBUG", args); } // Ref: https://github.com/sirupsen/logrus#level-logging -export type LogrusLogLevel = keyof (typeof LogrusLogLevels); +export type LogrusLogLevel = keyof typeof LogrusLogLevels; export const LogrusLogLevels = { trace: true, debug: true, @@ -182,7 +186,7 @@ export const LogrusLogLevels = { error: true, fatal: true, panic: true, -} +}; export namespace LogrusLogLevel { export function isGreatherOrEqual(lvl: LogrusLogLevel | undefined, ref: LogrusLogLevel | undefined): boolean { if (lvl === undefined) { @@ -194,8 +198,7 @@ export namespace LogrusLogLevel { return getLevelArity(lvl) >= getLevelArity(ref); } function getLevelArity(lvl: LogrusLogLevel): number { - return Object.keys(LogrusLogLevels) - .findIndex((l) => l === lvl); + return Object.keys(LogrusLogLevels).findIndex((l) => l === lvl); } export function getFromEnv(): LogrusLogLevel | undefined { const lvlStr = process.env.LOG_LEVEL; @@ -203,7 +206,7 @@ export namespace LogrusLogLevel { return undefined; } const lvl = lvlStr as LogrusLogLevel; - const exists = LogrusLogLevels[lvl] + const exists = LogrusLogLevels[lvl]; if (!exists) { return undefined; } @@ -212,12 +215,12 @@ export namespace LogrusLogLevel { } // Source: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity -type GoogleLogSeverity = 'EMERGENCY' | 'ALERT' | 'CRITICAL' | 'ERROR' | 'WARNING' | 'INFO' | 'DEBUG'; +type GoogleLogSeverity = "EMERGENCY" | "ALERT" | "CRITICAL" | "ERROR" | "WARNING" | "INFO" | "DEBUG"; namespace GoogleLogSeverity { export const isGreaterOrEqualThanWarning = (severity: GoogleLogSeverity) => { switch (severity) { - case 'INFO': - case 'DEBUG': + case "INFO": + case "DEBUG": return false; default: return true; @@ -245,7 +248,7 @@ function doLog(calledViaConsole: boolean, consoleLog: ConsoleLog, severity: Goog // console.xyz(Error, ...any) / log.xyz(Error) / log.xyz(Error, LogPayload) error = args[0]; payloadArgs = args.slice(1); - } else if (typeof args[0] === 'string') { + } else if (typeof args[0] === "string") { message = args[0]; if (args.length < 2 || !(args[1] instanceof Error)) { // console.xyz(string) / console.xyz(string, !Error, ...any) / log.xyz(string) / log.xyz(string, LogPayload) @@ -265,7 +268,7 @@ function doLog(calledViaConsole: boolean, consoleLog: ConsoleLog, severity: Goog // log.xyz(LogContext, Error) / log.xyz(LogContext, Error, LogPayload) error = args[1]; payloadArgs = args.slice(2); - } else if (typeof args[1] === 'string') { + } else if (typeof args[1] === "string") { message = args[1]; if (args.length < 3 || !(args[2] instanceof Error)) { // log.xyz(LogContext, string) / log.xyz(LogContext, string, LogPayload) @@ -288,9 +291,14 @@ function doLog(calledViaConsole: boolean, consoleLog: ConsoleLog, severity: Goog } } -function makeLogItem(severity: GoogleLogSeverity, context: LogContext | undefined, message: string | undefined, - error: Error | undefined, payloadArgs: any[], calledViaConsole: boolean): string | undefined { - +function makeLogItem( + severity: GoogleLogSeverity, + context: LogContext | undefined, + message: string | undefined, + error: Error | undefined, + payloadArgs: any[], + calledViaConsole: boolean, +): string | undefined { if (context !== undefined && Object.keys(context).length == 0) { context = undefined; } @@ -312,7 +320,7 @@ function makeLogItem(severity: GoogleLogSeverity, context: LogContext | undefine message, error, payload, - loggedViaConsole: calledViaConsole ? true : undefined + loggedViaConsole: calledViaConsole ? true : undefined, }; let result: string = stringifyLogItem(logItem); @@ -323,11 +331,11 @@ function makeLogItem(severity: GoogleLogSeverity, context: LogContext | undefine result = stringifyLogItem(logItem); if (result.length <= maxAllowedLogItemLength) { - log.warn('Log item too large, stripping payload', { logItemStub: makeLogItemStub(logItem) }); + log.warn("Log item too large, stripping payload", { logItemStub: makeLogItemStub(logItem) }); } } if (result.length > maxAllowedLogItemLength) { - log.error('Log item too large w/o payload, discarding', { logItemStub: makeLogItemStub(logItem) }); + log.error("Log item too large w/o payload, discarding", { logItemStub: makeLogItemStub(logItem) }); return undefined; } @@ -341,10 +349,10 @@ function makeReportedErrorEvent(error: Error | undefined) { // Serves as marker only "@type": "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent", // This is useful for filtering in the UI - "serviceContext": { - "service": component || "", - "version": version || "", - } + serviceContext: { + service: component || "", + version: version || "", + }, }; if (error) { @@ -364,9 +372,9 @@ function makeLogItemStub(logItem: any): any { severity: logItem.severity, time: logItem.time, environment: logItem.environment, - region: logItem.region + region: logItem.region, }; - if (typeof (logItem.message) === 'string') { + if (typeof logItem.message === "string") { if (logItem.message.length <= maxMessageStubLength) { result.message = logItem.message; } else { @@ -400,10 +408,12 @@ function stringifyLogItem(logItem: any): string { * Jsonifies Errors properly, not as {} only. */ function jsonStringifyWithErrors(value: any): string { - return JSON.stringify(value, (key: string, value: any): any => { return value instanceof Error ? value.stack : value }); + return JSON.stringify(value, (key: string, value: any): any => { + return value instanceof Error ? value.stack : value; + }); } -type ConsoleLog = (message?: any, ...optionalArgs: any[]) => void; // signature of console.xyz +type ConsoleLog = (message?: any, ...optionalArgs: any[]) => void; // signature of console.xyz const logConsoleLog: ConsoleLog = console.log; const errorConsoleLog: ConsoleLog = console.error; const warnConsoleLog: ConsoleLog = console.warn; diff --git a/components/gitpod-protocol/src/util/make-link.ts b/components/gitpod-protocol/src/util/make-link.ts index d931078d6530ae..9b532d7f33be6f 100644 --- a/components/gitpod-protocol/src/util/make-link.ts +++ b/components/gitpod-protocol/src/util/make-link.ts @@ -5,18 +5,17 @@ */ function isOpenNewTab(event: MouseEvent): boolean { - return event.metaKey || (event.ctrlKey); + return event.metaKey || event.ctrlKey; } export function makeLink(node: HTMLElement, url: string, hover: string): void { - node.onclick = event => { - let target = '_self'; + node.onclick = (event) => { + let target = "_self"; if (isOpenNewTab(event)) { - target = '_blank'; + target = "_blank"; } window.open(url, target); - } - node.style.cursor = 'pointer'; + }; + node.style.cursor = "pointer"; node.title = hover; } - diff --git a/components/gitpod-protocol/src/util/parse-workspace-id.spec.ts b/components/gitpod-protocol/src/util/parse-workspace-id.spec.ts index a3d2e82bd22e70..626e3242b594ac 100644 --- a/components/gitpod-protocol/src/util/parse-workspace-id.spec.ts +++ b/components/gitpod-protocol/src/util/parse-workspace-id.spec.ts @@ -4,14 +4,17 @@ * See License-AGPL.txt in the project root for license information. */ -import * as chai from 'chai'; -import { suite, test } from 'mocha-typescript'; -import { matchesInstanceIdOrLegacyWorkspaceIdExactly, matchesNewWorkspaceIdExactly, parseWorkspaceIdFromHostname } from './parse-workspace-id'; +import * as chai from "chai"; +import { suite, test } from "mocha-typescript"; +import { + matchesInstanceIdOrLegacyWorkspaceIdExactly, + matchesNewWorkspaceIdExactly, + parseWorkspaceIdFromHostname, +} from "./parse-workspace-id"; const expect = chai.expect; @suite export class ParseWorkspaceIdTest { - @test public parseWorkspaceIdFromHostname_fromWorkspaceLocation() { const actual = parseWorkspaceIdFromHostname("moccasin-ferret-155799b3.ws-eu01.gitpod.io"); expect(actual).to.equal("moccasin-ferret-155799b3"); @@ -28,7 +31,9 @@ export class ParseWorkspaceIdTest { } @test public parseWorkspaceIdFromHostname_fromWorkspacePortLocationWithWebviewPrefixCustomHost() { - const actual = parseWorkspaceIdFromHostname("webview-3000-moccasin-ferret-155799b3.ws-eu01.some.subdomain.somehost.com"); + const actual = parseWorkspaceIdFromHostname( + "webview-3000-moccasin-ferret-155799b3.ws-eu01.some.subdomain.somehost.com", + ); expect(actual).to.equal("moccasin-ferret-155799b3"); } @@ -44,12 +49,16 @@ export class ParseWorkspaceIdTest { } @test public parseLegacyWorkspaceIdFromHostname_fromWorkspacePortLocationWithWebviewPrefix() { - const actual = parseWorkspaceIdFromHostname("webview-3000-b7e0eaf8-ec73-44ec-81ea-04859263b656.ws-eu01.gitpod.io"); + const actual = parseWorkspaceIdFromHostname( + "webview-3000-b7e0eaf8-ec73-44ec-81ea-04859263b656.ws-eu01.gitpod.io", + ); expect(actual).to.equal("b7e0eaf8-ec73-44ec-81ea-04859263b656"); } @test public parseLegacyWorkspaceIdFromHostname_fromWorkspacePortLocationWithWebviewPrefixCustomHost() { - const actual = parseWorkspaceIdFromHostname("webview-3000-ca81a50f-09d7-465c-acd9-264a747d5351.ws-eu01.some.subdomain.somehost.com"); + const actual = parseWorkspaceIdFromHostname( + "webview-3000-ca81a50f-09d7-465c-acd9-264a747d5351.ws-eu01.some.subdomain.somehost.com", + ); expect(actual).to.equal("ca81a50f-09d7-465c-acd9-264a747d5351"); } @@ -73,4 +82,4 @@ export class ParseWorkspaceIdTest { expect(actual).to.be.false; } } -module.exports = new ParseWorkspaceIdTest() \ No newline at end of file +module.exports = new ParseWorkspaceIdTest(); diff --git a/components/gitpod-protocol/src/util/parse-workspace-id.ts b/components/gitpod-protocol/src/util/parse-workspace-id.ts index 99e63beeaf1c0b..53b36ae6af4370 100644 --- a/components/gitpod-protocol/src/util/parse-workspace-id.ts +++ b/components/gitpod-protocol/src/util/parse-workspace-id.ts @@ -20,7 +20,7 @@ const REGEX_WORKSPACE_ID_LEGACY_FROM_HOSTNAME = new RegExp(`(${REGEX_WORKSPACE_I * - webview-1234-moccasin-ferret-155799b3.ws-eu01.gitpod.io (or any other string replacing webview) * @param hostname The hostname the request is headed to */ -export const parseWorkspaceIdFromHostname = function(hostname: string) { +export const parseWorkspaceIdFromHostname = function (hostname: string) { const match = REGEX_WORKSPACE_ID_FROM_HOSTNAME.exec(hostname); if (match && match.length >= 2) { return match[1]; @@ -41,15 +41,14 @@ const REGEX_INSTANCE_ID_EXACT = new RegExp(`^${REGEX_INSTANCE_ID.source}$`); * @param maybeId * @returns */ -export const matchesInstanceIdOrLegacyWorkspaceIdExactly = function(maybeId: string): boolean { - return REGEX_INSTANCE_ID_EXACT.test(maybeId) - || REGEX_WORKSPACE_ID_LEGACY_EXACT.test(maybeId); +export const matchesInstanceIdOrLegacyWorkspaceIdExactly = function (maybeId: string): boolean { + return REGEX_INSTANCE_ID_EXACT.test(maybeId) || REGEX_WORKSPACE_ID_LEGACY_EXACT.test(maybeId); }; /** * @param maybeWorkspaceId * @returns */ -export const matchesNewWorkspaceIdExactly = function(maybeWorkspaceId: string): boolean { +export const matchesNewWorkspaceIdExactly = function (maybeWorkspaceId: string): boolean { return REGEX_WORKSPACE_ID_EXACT.test(maybeWorkspaceId); -}; \ No newline at end of file +}; diff --git a/components/gitpod-protocol/src/util/queue.spec.ts b/components/gitpod-protocol/src/util/queue.spec.ts index 14b597fab8b162..90b9793a1cb631 100644 --- a/components/gitpod-protocol/src/util/queue.spec.ts +++ b/components/gitpod-protocol/src/util/queue.spec.ts @@ -4,19 +4,19 @@ * See License-AGPL.txt in the project root for license information. */ -import { suite, test, slow, timeout } from 'mocha-typescript' -import * as chai from 'chai' -const chaiSubset = require('chai-subset'); +import { suite, test, slow, timeout } from "mocha-typescript"; +import * as chai from "chai"; +const chaiSubset = require("chai-subset"); chai.use(chaiSubset); -import { Queue } from '..'; -import { fail } from 'assert'; -import { Deferred } from './deferred'; +import { Queue } from ".."; +import { fail } from "assert"; +import { Deferred } from "./deferred"; -const expect = chai.expect - -@suite class QueueSpec { +const expect = chai.expect; +@suite +class QueueSpec { queue: Queue; seq: number[]; @@ -35,8 +35,7 @@ const expect = chai.expect resolve(undefined); }, sleep); }); - else - this.seq.push(seqNr); + else this.seq.push(seqNr); }; if (nextTick) @@ -45,20 +44,22 @@ const expect = chai.expect push().then(resolve); }); }); - else - await push(); + else await push(); }); } execError(seqNr: number): Deferred { const deferred = new Deferred(); - this.queue.enqueue(async () => { - this.seq.push(seqNr); - throw new Error('test error'); - }).then(() => { - deferred.reject(false); - }).catch(() => { - deferred.resolve(true); - }); + this.queue + .enqueue(async () => { + this.seq.push(seqNr); + throw new Error("test error"); + }) + .then(() => { + deferred.reject(false); + }) + .catch(() => { + deferred.resolve(true); + }); return deferred; } @@ -67,7 +68,9 @@ const expect = chai.expect expect(actual).to.have.lengthOf(expected.length); const expIt = expected.entries(); for (const act of actual) { - const { value: [, exp] } = expIt.next(); + const { + value: [, exp], + } = expIt.next(); expect(act).to.deep.equal(exp); } } @@ -93,7 +96,6 @@ const expect = chai.expect this.expectArray(this.seq, [1, 2]); } - @test public async continueDespiteError() { this.exec(1); const receivedError = this.execError(2); @@ -107,7 +109,7 @@ const expect = chai.expect @test public async mustCatchError() { const f = async () => { throw new Error(); - } + }; try { const p = this.queue.enqueue(async () => { return f(); @@ -124,7 +126,7 @@ const expect = chai.expect @test public async expectUncaughtError() { const f = async () => { throw new Error(); - } + }; const p = this.queue.enqueue(async () => { return f(); }); diff --git a/components/gitpod-protocol/src/util/repeat.ts b/components/gitpod-protocol/src/util/repeat.ts index 17c352d472556f..773f7a02680aa3 100644 --- a/components/gitpod-protocol/src/util/repeat.ts +++ b/components/gitpod-protocol/src/util/repeat.ts @@ -42,4 +42,4 @@ export function repeat(op: () => Promise | void, everyMilliseconds: number clearTimeout(timer); } }); -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/semaphore.ts b/components/gitpod-protocol/src/util/semaphore.ts index 07397d65408019..468ee4bd15ccd4 100644 --- a/components/gitpod-protocol/src/util/semaphore.ts +++ b/components/gitpod-protocol/src/util/semaphore.ts @@ -4,19 +4,18 @@ * See License-AGPL.txt in the project root for license information. */ - export class Semaphore { protected queue: (() => void)[] = []; protected used: number; constructor(protected readonly capacity: number) { - if(capacity < 1) { + if (capacity < 1) { throw new Error("Capacity cannot be less than 1"); } } public release() { - if(this.used == 0) return; + if (this.used == 0) return; const queued = this.queue.shift(); if (queued) { @@ -28,7 +27,7 @@ export class Semaphore { public async acquire(): Promise { this.used++; - if(this.used <= this.capacity) { + if (this.used <= this.capacity) { return Promise.resolve(); } @@ -36,5 +35,4 @@ export class Semaphore { this.queue.push(rs); }); } - -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/skip-if.ts b/components/gitpod-protocol/src/util/skip-if.ts index 37bb27742d2c70..b35ce70cdaa4c6 100644 --- a/components/gitpod-protocol/src/util/skip-if.ts +++ b/components/gitpod-protocol/src/util/skip-if.ts @@ -4,7 +4,6 @@ * See License-AGPL.txt in the project root for license information. */ - /** * The subset of actually available fields and methods which are not exported but we care about */ @@ -18,14 +17,18 @@ interface TestSuiteContext extends Mocha.ISuiteCallbackContext { * @param doSkip A function which takes a TestSuite and decides if it should be skipped */ export function skipIf(doSkip: (suite: TestSuiteContext) => boolean): MochaTypeScript.SuiteTrait { - const trait: MochaTypeScript.SuiteTrait = function(this: Mocha.ISuiteCallbackContext, ctx: Mocha.ISuiteCallbackContext, ctor: Function): void { - const suite = ctx as any as TestSuiteContext; // No idea why those fields are not exported in the types + const trait: MochaTypeScript.SuiteTrait = function ( + this: Mocha.ISuiteCallbackContext, + ctx: Mocha.ISuiteCallbackContext, + ctor: Function, + ): void { + const suite = ctx as any as TestSuiteContext; // No idea why those fields are not exported in the types const skip = doSkip(suite); - suite.beforeEach(function(this: Mocha.IHookCallbackContext) { + suite.beforeEach(function (this: Mocha.IHookCallbackContext) { if (skip) { this.skip(); } - }) + }); }; // Mark as "trait": mimics the behavior of https://github.com/testdeck/testdeck/blob/9d2dd6a458c2c86c945f6f2999b8278b7528a7a7/index.ts#L433 @@ -45,4 +48,4 @@ export function skipIfEnvVarNotSet(name: string): MochaTypeScript.SuiteTrait { } return skip; }); -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/timeutil.spec.ts b/components/gitpod-protocol/src/util/timeutil.spec.ts index c84ef72ebf69f3..232a4ae44982bc 100644 --- a/components/gitpod-protocol/src/util/timeutil.spec.ts +++ b/components/gitpod-protocol/src/util/timeutil.spec.ts @@ -4,40 +4,39 @@ * See License-AGPL.txt in the project root for license information. */ -import * as chai from 'chai'; +import * as chai from "chai"; const expect = chai.expect; -import { suite, test } from 'mocha-typescript'; -import { oneMonthLater } from './timeutil'; +import { suite, test } from "mocha-typescript"; +import { oneMonthLater } from "./timeutil"; @suite() export class TimeutilSpec { - @test testTimeutil() { // targeting a 1st, 1th of Jan => 1st of Feb - this.isOneMonthLater(new Date(2000, 0, 1), 1, new Date(2000, 1, 1)) + this.isOneMonthLater(new Date(2000, 0, 1), 1, new Date(2000, 1, 1)); // targeting a 31th, 30th of Apr => 31st of May - this.isOneMonthLater(new Date(2000, 3, 30), 31, new Date(2000, 4, 31)) + this.isOneMonthLater(new Date(2000, 3, 30), 31, new Date(2000, 4, 31)); // targeting a 31th, 31th of Mar => 30th of Apr - this.isOneMonthLater(new Date(2000, 2, 31), 31, new Date(2000, 3, 30)) + this.isOneMonthLater(new Date(2000, 2, 31), 31, new Date(2000, 3, 30)); // targeting a 30th, 30th of Mar => 30th of Apr - this.isOneMonthLater(new Date(2000, 2, 30), 30, new Date(2000, 3, 30)) + this.isOneMonthLater(new Date(2000, 2, 30), 30, new Date(2000, 3, 30)); // next year - this.isOneMonthLater(new Date(2000, 11, 1), 1, new Date(2001, 0, 1)) - this.isOneMonthLater(new Date(2000, 11, 31), 31, new Date(2001, 0, 31)) + this.isOneMonthLater(new Date(2000, 11, 1), 1, new Date(2001, 0, 1)); + this.isOneMonthLater(new Date(2000, 11, 31), 31, new Date(2001, 0, 31)); // Feb - this.isOneMonthLater(new Date(2001, 0, 31), 31, new Date(2001, 1, 28)) + this.isOneMonthLater(new Date(2001, 0, 31), 31, new Date(2001, 1, 28)); // Feb leap year - this.isOneMonthLater(new Date(2000, 0, 31), 31, new Date(2000, 1, 29)) + this.isOneMonthLater(new Date(2000, 0, 31), 31, new Date(2000, 1, 29)); } isOneMonthLater(from: Date, day: number, expectation: Date) { - const later = oneMonthLater(from.toISOString(), day) - expect(later, `expected ${later} to be equal ${expectation}`).to.be.equal(expectation.toISOString()) + const later = oneMonthLater(from.toISOString(), day); + expect(later, `expected ${later} to be equal ${expectation}`).to.be.equal(expectation.toISOString()); } -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/util/timeutil.ts b/components/gitpod-protocol/src/util/timeutil.ts index 7a3471eabeceac..ca54246b1c9646 100644 --- a/components/gitpod-protocol/src/util/timeutil.ts +++ b/components/gitpod-protocol/src/util/timeutil.ts @@ -24,28 +24,32 @@ export function oneMonthLater(fromDate: string, day?: number): string { } return later.toISOString(); } -export const yearsLater = (fromDate: string, years: number): string => liftDate1(fromDate, (d) => { - d.setUTCFullYear(d.getUTCFullYear() + years); - return d.toISOString(); -}); +export const yearsLater = (fromDate: string, years: number): string => + liftDate1(fromDate, (d) => { + d.setUTCFullYear(d.getUTCFullYear() + years); + return d.toISOString(); + }); // tslint:disable-next-line:no-shadowed-variable -export const addMillis = (d1: string, millis: number) => liftDate1(d1, (d1) => new Date(d1.getTime() + millis).toISOString()); -export const durationInHours = (d1: string, d2: string) => liftDate(d1, d2, (d1, d2) => millisecondsToHours(d1.getTime() - d2.getTime())); +export const addMillis = (d1: string, millis: number) => + liftDate1(d1, (d1) => new Date(d1.getTime() + millis).toISOString()); +export const durationInHours = (d1: string, d2: string) => + liftDate(d1, d2, (d1, d2) => millisecondsToHours(d1.getTime() - d2.getTime())); export const durationInMillis = (d1: string, d2: string) => liftDate(d1, d2, (d1, d2) => d1.getTime() - d2.getTime()); // tslint:disable-next-line:no-shadowed-variable -export const isDateGreaterOrEqual = (d1: string, d2: string): boolean => liftDate(d1, d2, (d1, d2) => d1.getTime() >= d2.getTime()); +export const isDateGreaterOrEqual = (d1: string, d2: string): boolean => + liftDate(d1, d2, (d1, d2) => d1.getTime() >= d2.getTime()); export const isDateSmallerOrEqual = (d1: string, d2: string | undefined) => !d2 || d1 <= d2; export const isDateSmaller = (d1: string, d2: string | undefined) => !d2 || d1 < d2; -export const oldest = (d1: string, d2: string): string => d1 > d2 ? d1 : d2; -export const earliest = (d1: string, d2: string): string => d1 < d2 ? d1 : d2; +export const oldest = (d1: string, d2: string): string => (d1 > d2 ? d1 : d2); +export const earliest = (d1: string, d2: string): string => (d1 < d2 ? d1 : d2); export const orderAsc = (d1: string, d2: string): number => liftDate(d1, d2, (d1, d2) => d1.getTime() - d2.getTime()); export const liftDate1 = (d1: string, f: (d1: Date) => T): T => f(new Date(d1)); export const liftDate = (d1: string, d2: string, f: (d1: Date, d2: Date) => T): T => f(new Date(d1), new Date(d2)); export function hoursLater(date: string, hours: number): string { - const result = new Date(date) - result.setHours(result.getHours() + hours) + const result = new Date(date); + result.setHours(result.getHours() + hours); return result.toISOString(); } diff --git a/components/gitpod-protocol/src/util/tracing.spec.ts b/components/gitpod-protocol/src/util/tracing.spec.ts index 75f14eb251ca77..b3c5c71ad14755 100644 --- a/components/gitpod-protocol/src/util/tracing.spec.ts +++ b/components/gitpod-protocol/src/util/tracing.spec.ts @@ -4,28 +4,31 @@ * See License-AGPL.txt in the project root for license information. */ -import { suite, test } from "mocha-typescript" -import * as chai from "chai" +import { suite, test } from "mocha-typescript"; +import * as chai from "chai"; import { TraceContext } from "./tracing"; import { MockTracer } from "opentracing"; -const expect = chai.expect - -@suite class TestTracing { +const expect = chai.expect; +@suite +class TestTracing { @test public async testTracingContext_addNestedTags() { const tracer = new MockTracer(); - const span = tracer.startSpan('testTracingContext_addNestedTags'); - TraceContext.addNestedTags({ span }, { - rpc: { - system: "jsonrpc", - jsonrpc: { - version: "1.0", - method: "test", - parameters: ["abc", "def"], + const span = tracer.startSpan("testTracingContext_addNestedTags"); + TraceContext.addNestedTags( + { span }, + { + rpc: { + system: "jsonrpc", + jsonrpc: { + version: "1.0", + method: "test", + parameters: ["abc", "def"], + }, }, }, - }); + ); const mockSpan = tracer.report().spans[0]; expect(mockSpan.tags()).to.deep.equal({ @@ -39,13 +42,16 @@ const expect = chai.expect @test public async testTracingContext_addNestedTags_null() { const tracer = new MockTracer(); - const span = tracer.startSpan('testTracingContext_addNestedTags_null'); - TraceContext.addNestedTags({ span }, { - someShape: { - thisIsNull: null, - thisIsUndefined: undefined, + const span = tracer.startSpan("testTracingContext_addNestedTags_null"); + TraceContext.addNestedTags( + { span }, + { + someShape: { + thisIsNull: null, + thisIsUndefined: undefined, + }, }, - }); + ); const mockSpan = tracer.report().spans[0]; expect(mockSpan.tags()).to.deep.equal({ @@ -56,7 +62,7 @@ const expect = chai.expect @test public async testTracingContext_addJsonRPCParameters() { const tracer = new MockTracer(); - const span = tracer.startSpan('testTracingContext_addJsonRPCParameters'); + const span = tracer.startSpan("testTracingContext_addJsonRPCParameters"); const ctx = { span }; TraceContext.addJsonRPCParameters(ctx, { one: "one", @@ -78,6 +84,5 @@ const expect = chai.expect "rpc.system": "jsonrpc", }); } - } -module.exports = new TestTracing() +module.exports = new TestTracing(); diff --git a/components/gitpod-protocol/src/util/tracing.ts b/components/gitpod-protocol/src/util/tracing.ts index b204904cba7b20..89d0ffe604b748 100644 --- a/components/gitpod-protocol/src/util/tracing.ts +++ b/components/gitpod-protocol/src/util/tracing.ts @@ -4,22 +4,20 @@ * See License-AGPL.txt in the project root for license information. */ - -import * as opentracing from 'opentracing'; -import { TracingConfig, initTracerFromEnv } from 'jaeger-client'; -import { Sampler, SamplingDecision } from './jaeger-client-types'; -import { initGlobalTracer } from 'opentracing'; -import { injectable } from 'inversify'; -import { ResponseError } from 'vscode-jsonrpc'; -import { log, LogContext } from './logging'; +import * as opentracing from "opentracing"; +import { TracingConfig, initTracerFromEnv } from "jaeger-client"; +import { Sampler, SamplingDecision } from "./jaeger-client-types"; +import { initGlobalTracer } from "opentracing"; +import { injectable } from "inversify"; +import { ResponseError } from "vscode-jsonrpc"; +import { log, LogContext } from "./logging"; export interface TraceContext { - span?: opentracing.Span + span?: opentracing.Span; } export type TraceContextWithSpan = TraceContext & { - span: opentracing.Span -} - + span: opentracing.Span; +}; export namespace TraceContext { export function startSpan(operation: string, parentCtx?: TraceContext): opentracing.Span { @@ -53,9 +51,9 @@ export namespace TraceContext { const span = TraceContext.startSpan(operation, ctx); try { - callback({span}); + callback({ span }); } catch (e) { - TraceContext.setError({span}, e); + TraceContext.setError({ span }, e); throw e; } finally { span.finish(); @@ -93,7 +91,12 @@ export namespace TraceContext { addNestedTags(ctx, tags); } - export function setJsonRPCError(ctx: TraceContext, method: string, err: ResponseError, withStatusCode: boolean = false) { + export function setJsonRPCError( + ctx: TraceContext, + method: string, + err: ResponseError, + withStatusCode: boolean = false, + ) { if (!ctx.span) { return; } @@ -160,7 +163,7 @@ export namespace TraceContext { if (!ctx.span) { return; } - const namespace = _namespace ? `${_namespace}.` : ''; + const namespace = _namespace ? `${_namespace}.` : ""; try { for (const k of Object.keys(keyValueMap)) { @@ -189,7 +192,6 @@ export namespace TraceContext { @injectable() export class TracingManager { - public setup(serviceName: string, opts?: CustomTracerOpts) { initGlobalTracer(this.getTracerForService(serviceName, opts)); } @@ -198,16 +200,16 @@ export class TracingManager { const config: TracingConfig = { disable: false, reporter: { - logSpans: false + logSpans: false, }, serviceName, - } + }; const t = initTracerFromEnv(config, { logger: console, tags: { - 'service.build.commit': process.env.GITPOD_BUILD_GIT_COMMIT, - 'service.build.version': process.env.GITPOD_BUILD_VERSION, - } + "service.build.commit": process.env.GITPOD_BUILD_GIT_COMMIT, + "service.build.version": process.env.GITPOD_BUILD_VERSION, + }, }); if (opts) { @@ -217,24 +219,22 @@ export class TracingManager { } return t; } - } export interface CustomTracerOpts { - perOpSampling?: PerOperationSampling + perOpSampling?: PerOperationSampling; } - // has to conform to https://github.com/jaegertracing/jaeger-client-node/blob/0042b1c0a0796bb655eb93e77ff76ab5e94c2bb6/src/_flow/sampler-thrift.js#L32 export interface PerOperationSampling { - [key: string]: boolean + [key: string]: boolean; } export class PerOperationSampler implements Sampler { constructor(protected readonly fallback: Sampler, protected readonly strategies: PerOperationSampling) {} name(): string { - return 'PerOperationSampler'; + return "PerOperationSampler"; } toString(): string { @@ -274,7 +274,7 @@ export class PerOperationSampler implements Sampler { return false; // TODO equal should be removed } - close(callback: ()=>void): void { + close(callback: () => void): void { // all nested samplers are of simple types, so we do not need to Close them if (callback) { callback(); @@ -283,18 +283,23 @@ export class PerOperationSampler implements Sampler { } // Augment interfaces with an leading parameter "TraceContext" on every method -type IsValidArg = T extends object ? keyof T extends never ? false : true : true; -type AddTraceContext = - T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F) => infer R ? ( - IsValidArg extends true ? (ctx: TraceContextWithSpan, a: A, b: B, c: C, d: D, e: E, f: F) => R : - IsValidArg extends true ? (ctx: TraceContextWithSpan, a: A, b: B, c: C, d: D, e: E) => R : - IsValidArg extends true ? (ctx: TraceContextWithSpan, a: A, b: B, c: C, d: D) => R : - IsValidArg extends true ? (ctx: TraceContextWithSpan, a: A, b: B, c: C) => R : - IsValidArg extends true ? (ctx: TraceContextWithSpan, a: A, b: B) => R : - IsValidArg extends true ? (ctx: TraceContextWithSpan, a: A) => R : - (ctx: TraceContextWithSpan) => Promise - ) : never; +type IsValidArg = T extends object ? (keyof T extends never ? false : true) : true; +type AddTraceContext = T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F) => infer R + ? IsValidArg extends true + ? (ctx: TraceContextWithSpan, a: A, b: B, c: C, d: D, e: E, f: F) => R + : IsValidArg extends true + ? (ctx: TraceContextWithSpan, a: A, b: B, c: C, d: D, e: E) => R + : IsValidArg extends true + ? (ctx: TraceContextWithSpan, a: A, b: B, c: C, d: D) => R + : IsValidArg extends true + ? (ctx: TraceContextWithSpan, a: A, b: B, c: C) => R + : IsValidArg extends true + ? (ctx: TraceContextWithSpan, a: A, b: B) => R + : IsValidArg extends true + ? (ctx: TraceContextWithSpan, a: A) => R + : (ctx: TraceContextWithSpan) => Promise + : never; export type InterfaceWithTraceContext = { - [P in keyof T]: AddTraceContext -}; \ No newline at end of file + [P in keyof T]: AddTraceContext; +}; diff --git a/components/gitpod-protocol/src/util/workspace-port-authentication.ts b/components/gitpod-protocol/src/util/workspace-port-authentication.ts index 16882c3137a65a..b8fa2fc331ee13 100644 --- a/components/gitpod-protocol/src/util/workspace-port-authentication.ts +++ b/components/gitpod-protocol/src/util/workspace-port-authentication.ts @@ -5,16 +5,13 @@ */ /** -* These cookies are set in the Theia frontend. This pattern is relied upon in: -* - proxy: -* - to filter it out on port locations -* - to forward it to the server for authentication -* - server: -* - to authenticate access to port locations -*/ -export const worspacePortAuthCookieName = function(host: string, workspaceId: string): string { - return host - .replace(/https?/, '') - .replace(/[\W_]+/g, "_") - + `_ws_${workspaceId}_port_auth_`; -}; \ No newline at end of file + * These cookies are set in the Theia frontend. This pattern is relied upon in: + * - proxy: + * - to filter it out on port locations + * - to forward it to the server for authentication + * - server: + * - to authenticate access to port locations + */ +export const worspacePortAuthCookieName = function (host: string, workspaceId: string): string { + return host.replace(/https?/, "").replace(/[\W_]+/g, "_") + `_ws_${workspaceId}_port_auth_`; +}; diff --git a/components/gitpod-protocol/src/workspace-cluster.ts b/components/gitpod-protocol/src/workspace-cluster.ts index 7e158080d71390..2ab034a52f19c5 100644 --- a/components/gitpod-protocol/src/workspace-cluster.ts +++ b/components/gitpod-protocol/src/workspace-cluster.ts @@ -4,10 +4,10 @@ * See License-AGPL.txt in the project root for license information. */ -import * as fs from 'fs'; -import { filePathTelepresenceAware } from './env'; +import * as fs from "fs"; +import { filePathTelepresenceAware } from "./env"; import { DeepPartial } from "./util/deep-partial"; -import { PermissionName } from './permission'; +import { PermissionName } from "./permission"; export interface WorkspaceCluster { // Name of the workspace cluster. @@ -47,25 +47,28 @@ export interface TLSConfig { crt: string; } export namespace TLSConfig { - export const loadFromBase64File = (path: string): string => fs.readFileSync(filePathTelepresenceAware(path)).toString("base64"); + export const loadFromBase64File = (path: string): string => + fs.readFileSync(filePathTelepresenceAware(path)).toString("base64"); } export type WorkspaceClusterWoTLS = Omit; export type WorkspaceManagerConnectionInfo = Pick; -export type AdmissionConstraint = AdmissionConstraintFeaturePreview | AdmissionConstraintHasPermission | AdmissionConstraintHasUserLevel | AdmissionConstraintHasMoreResources; +export type AdmissionConstraint = + | AdmissionConstraintFeaturePreview + | AdmissionConstraintHasPermission + | AdmissionConstraintHasUserLevel + | AdmissionConstraintHasMoreResources; export type AdmissionConstraintFeaturePreview = { type: "has-feature-preview" }; -export type AdmissionConstraintHasPermission = { type: "has-permission", permission: PermissionName }; -export type AdmissionConstraintHasUserLevel = { type: "has-user-level", level: string }; +export type AdmissionConstraintHasPermission = { type: "has-permission"; permission: PermissionName }; +export type AdmissionConstraintHasUserLevel = { type: "has-user-level"; level: string }; export type AdmissionConstraintHasMoreResources = { type: "has-more-resources" }; export namespace AdmissionConstraint { export function is(o: any): o is AdmissionConstraint { - return !!o - && 'type' in o; + return !!o && "type" in o; } export function isHasPermissionConstraint(o: any): o is AdmissionConstraintHasPermission { - return is(o) - && o.type === "has-permission"; + return is(o) && o.type === "has-permission"; } export function hasPermission(ac: AdmissionConstraint, permission: PermissionName): boolean { return isHasPermissionConstraint(ac) && ac.permission === permission; @@ -101,4 +104,4 @@ export interface WorkspaceClusterDB { } export interface WorkspaceClusterFilter extends Pick { minScore: number; -} \ No newline at end of file +} diff --git a/components/gitpod-protocol/src/wsready.ts b/components/gitpod-protocol/src/wsready.ts index 1329047705ed1d..87dcda535f8151 100644 --- a/components/gitpod-protocol/src/wsready.ts +++ b/components/gitpod-protocol/src/wsready.ts @@ -13,5 +13,5 @@ export enum WorkspaceInitSource { WorkspaceInitFromOther = "from-other", } export interface WorkspaceReadyMessage { - source: WorkspaceInitSource + source: WorkspaceInitSource; } diff --git a/components/server/ee/src/prebuilds/prebuild-manager.ts b/components/server/ee/src/prebuilds/prebuild-manager.ts index a80fea00e44e73..6f30bbe0f3d446 100644 --- a/components/server/ee/src/prebuilds/prebuild-manager.ts +++ b/components/server/ee/src/prebuilds/prebuild-manager.ts @@ -4,6 +4,7 @@ * See License.enterprise.txt in the project root folder. */ +<<<<<<< HEAD import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB } from "@gitpod/gitpod-db/lib"; import { CommitContext, @@ -31,6 +32,29 @@ import { secondsBefore } from "@gitpod/gitpod-protocol/lib/util/timeutil"; import { inject, injectable } from "inversify"; import * as opentracing from "opentracing"; +======= +import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB } from '@gitpod/gitpod-db/lib'; +import { CommitContext, CommitInfo, PrebuiltWorkspace, Project, ProjectEnvVar, StartPrebuildContext, StartPrebuildResult, TaskConfig, User, Workspace, WorkspaceConfig, WorkspaceInstance } from '@gitpod/gitpod-protocol'; +import { log } from '@gitpod/gitpod-protocol/lib/util/logging'; +import { TraceContext } from '@gitpod/gitpod-protocol/lib/util/tracing'; +import { getCommitInfo, HostContextProvider } from '../../../src/auth/host-context-provider'; +import { WorkspaceFactory } from '../../../src/workspace/workspace-factory'; +import { ConfigProvider } from '../../../src/workspace/config-provider'; +import { WorkspaceStarter } from '../../../src/workspace/workspace-starter'; +import { Config } from '../../../src/config'; +import { ProjectsService } from '../../../src/projects/projects-service'; +import { secondsBefore } from '@gitpod/gitpod-protocol/lib/util/timeutil'; + +import { inject, injectable } from 'inversify'; +import * as opentracing from 'opentracing'; +<<<<<<< HEAD +<<<<<<< HEAD +======= +import { URL } from 'url'; +>>>>>>> 3e7b850b (regen) +>>>>>>> 6359a743 (regen) +======= +>>>>>>> 67b9f012 (Regen) export class WorkspaceRunningError extends Error { constructor(msg: string, public instance: WorkspaceInstance) { @@ -153,6 +177,7 @@ export class PrebuildManager { const projectEnvVarsPromise = project ? this.projectService.getProjectEnvironmentVariables(project.id) : []; +<<<<<<< HEAD const workspace = await this.workspaceFactory.createForContext( { span }, user, @@ -166,6 +191,18 @@ export class PrebuildManager { await this.workspaceStarter.startWorkspace({ span }, workspace, user, [], projectEnvVars, { excludeFeatureFlags: ["full_workspace_backup"], }); +======= + const workspace = await this.workspaceFactory.createForContext({span}, user, prebuildContext, context.normalizedContextURL!); + const prebuildPromise = this.workspaceDB.trace({span}).findPrebuildByWorkspaceID(workspace.id)!; + + span.setTag("starting", true); + const projectEnvVars = await projectEnvVarsPromise; + await this.workspaceStarter.startWorkspace({ span }, workspace, user, [], projectEnvVars, {excludeFeatureFlags: ['full_workspace_backup']}); +<<<<<<< HEAD +<<<<<<< HEAD +>>>>>>> 6359a743 (regen) +======= +>>>>>>> 67b9f012 (Regen) const prebuild = await prebuildPromise; if (!prebuild) { throw new Error(`Failed to create a prebuild for: ${context.normalizedContextURL}`); @@ -347,4 +384,40 @@ export class PrebuildManager { // Last resort default return PREBUILD_LIMITER_DEFAULT_LIMIT; } +<<<<<<< HEAD +<<<<<<< HEAD +======= + + private async shouldRateLimitPrebuild(span: opentracing.Span, cloneURL: string): Promise { + const windowStart = secondsBefore(new Date().toISOString(), PREBUILD_LIMITER_WINDOW_SECONDS); + const unabortedCount = await this.workspaceDB.trace({span}).countUnabortedPrebuildsSince(cloneURL, new Date(windowStart)); + const limit = this.getPrebuildRateLimitForCloneURL(cloneURL); + + if (unabortedCount >= limit) { + log.debug("Prebuild exceeds rate limit", { limit, unabortedPrebuildsCount: unabortedCount, cloneURL }); + return true; + } + return false; + } + + private getPrebuildRateLimitForCloneURL(cloneURL: string): number { + // First we use any explicit overrides for a given cloneURL + let limit = this.config.prebuildLimiter[cloneURL]; + if (limit > 0) { + return limit; + } + + // Find if there is a default value set under the '*' key + limit = this.config.prebuildLimiter['*']; + if (limit > 0) { + return limit; + } + + // Last resort default + return PREBUILD_LIMITER_DEFAULT_LIMIT; + } +>>>>>>> 6359a743 (regen) +} +======= } +>>>>>>> 67b9f012 (Regen)