Removing Prisma and Grapql-nestjs-prisma resolvers (#2574)
* Some cleaning * Fix seeds * Fix all sign in, sign up flow and apiKey optimistic rendering * Fix
This commit is contained in:
@@ -1,344 +0,0 @@
|
||||
import {
|
||||
Command,
|
||||
CommandRunner,
|
||||
InquirerService,
|
||||
Option,
|
||||
} from 'nest-commander';
|
||||
import isEqual from 'lodash.isequal';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import peopleSeed from 'src/core/person/seed-data/people.json';
|
||||
import companiesSeed from 'src/core/company/seed-data/companies.json';
|
||||
import pipelineStagesSeed from 'src/core/pipeline/seed-data/pipeline-stages.json';
|
||||
import pipelinesSeed from 'src/core/pipeline/seed-data/sales-pipeline.json';
|
||||
import { WorkspaceService } from 'src/core/workspace/services/workspace.service';
|
||||
|
||||
interface DataCleanInactiveOptions {
|
||||
days?: number;
|
||||
sameAsSeedDays?: number;
|
||||
dryRun?: boolean;
|
||||
confirmation?: boolean;
|
||||
workspaceId?: string;
|
||||
}
|
||||
|
||||
interface ActivityReport {
|
||||
displayName: string;
|
||||
maxUpdatedAt: string;
|
||||
inactiveDays: number;
|
||||
sameAsSeed: boolean;
|
||||
}
|
||||
|
||||
interface DataCleanResults {
|
||||
[key: string]: ActivityReport;
|
||||
}
|
||||
|
||||
const formattedPipelineStagesSeed = pipelineStagesSeed.map((pipelineStage) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { position, ...rest } = pipelineStage;
|
||||
return rest;
|
||||
});
|
||||
|
||||
@Command({
|
||||
name: 'workspaces:clean-inactive',
|
||||
description: 'Clean inactive workspaces from the public database schema',
|
||||
})
|
||||
export class DataCleanInactiveCommand extends CommandRunner {
|
||||
constructor(
|
||||
private readonly prismaService: PrismaService,
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
private readonly inquiererService: InquirerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '-w, --workspaceId [workspace id]',
|
||||
description: 'Specific workspaceId to apply cleaning',
|
||||
})
|
||||
parseWorkspace(val: string): string {
|
||||
return val;
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '-d, --days [inactive days threshold]',
|
||||
description: 'Inactive days threshold',
|
||||
defaultValue: 60,
|
||||
})
|
||||
parseDays(val: string): number {
|
||||
return Number(val);
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '-s, --same-as-seed-days [same as seed days threshold]',
|
||||
description: 'Same as seed days threshold',
|
||||
defaultValue: 10,
|
||||
})
|
||||
parseSameAsSeedDays(val: string): number {
|
||||
return Number(val);
|
||||
}
|
||||
|
||||
@Option({
|
||||
flags: '--dry-run [dry run]',
|
||||
description: 'List inactive workspaces without removing them',
|
||||
})
|
||||
parseDryRun(val: string): boolean {
|
||||
return Boolean(val);
|
||||
}
|
||||
|
||||
// We look for public tables which contain workspaceId and updatedAt columns and exist in production database
|
||||
getRelevantTables() {
|
||||
return Object.keys(this.prismaService.client).filter(
|
||||
(name) =>
|
||||
!name.startsWith('_') &&
|
||||
!name.startsWith('$') &&
|
||||
!name.includes('user') &&
|
||||
!name.includes('refreshToken') &&
|
||||
!name.includes('workspace') &&
|
||||
!name.includes('favorite'),
|
||||
);
|
||||
}
|
||||
|
||||
async getMaxUpdatedAtForAllWorkspaces(tables, workspaces) {
|
||||
const result = {};
|
||||
for (const table of tables) {
|
||||
result[table] = {};
|
||||
const groupByWorkspaces = await this.prismaService.client[table].groupBy({
|
||||
by: ['workspaceId'],
|
||||
_max: { updatedAt: true },
|
||||
where: {
|
||||
workspaceId: { in: workspaces.map((workspace) => workspace.id) },
|
||||
},
|
||||
});
|
||||
for (const groupByWorkspace of groupByWorkspaces) {
|
||||
result[table][groupByWorkspace.workspaceId] =
|
||||
groupByWorkspace._max.updatedAt;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async addMaxUpdatedAtToWorkspaces(
|
||||
result,
|
||||
workspace,
|
||||
table,
|
||||
maxUpdatedAtForAllWorkspaces,
|
||||
) {
|
||||
const newUpdatedAt = maxUpdatedAtForAllWorkspaces[table][workspace.id];
|
||||
if (!result[workspace.id]) {
|
||||
result[workspace.id] = {
|
||||
displayName: workspace.displayName,
|
||||
maxUpdatedAt: null,
|
||||
};
|
||||
}
|
||||
if (
|
||||
newUpdatedAt &&
|
||||
new Date(result[workspace.id].maxUpdatedAt) < new Date(newUpdatedAt)
|
||||
) {
|
||||
result[workspace.id].maxUpdatedAt = newUpdatedAt;
|
||||
}
|
||||
}
|
||||
async getSeedTableData(workspaces) {
|
||||
const where = {
|
||||
workspaceId: { in: workspaces.map((workspace) => workspace.id) },
|
||||
};
|
||||
const companies = await this.prismaService.client.company.findMany({
|
||||
select: {
|
||||
name: true,
|
||||
domainName: true,
|
||||
address: true,
|
||||
employees: true,
|
||||
workspaceId: true,
|
||||
},
|
||||
where,
|
||||
});
|
||||
const people = await this.prismaService.client.person.findMany({
|
||||
select: {
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
city: true,
|
||||
email: true,
|
||||
avatarUrl: true,
|
||||
workspaceId: true,
|
||||
},
|
||||
where,
|
||||
});
|
||||
const pipelineStages =
|
||||
await this.prismaService.client.pipelineStage.findMany({
|
||||
select: {
|
||||
name: true,
|
||||
color: true,
|
||||
type: true,
|
||||
workspaceId: true,
|
||||
},
|
||||
where,
|
||||
});
|
||||
const pipelines = await this.prismaService.client.pipeline.findMany({
|
||||
select: {
|
||||
name: true,
|
||||
icon: true,
|
||||
pipelineProgressableType: true,
|
||||
workspaceId: true,
|
||||
},
|
||||
where,
|
||||
});
|
||||
return {
|
||||
companies,
|
||||
people,
|
||||
pipelineStages,
|
||||
pipelines,
|
||||
};
|
||||
}
|
||||
|
||||
async detectWorkspacesWithSeedDataOnly(result, workspace, seedTableData) {
|
||||
const companies = seedTableData.companies.reduce((filtered, company) => {
|
||||
if (company.workspaceId === workspace.id) {
|
||||
delete company.workspaceId;
|
||||
filtered.push(company);
|
||||
}
|
||||
return filtered;
|
||||
}, []);
|
||||
const people = seedTableData.people.reduce((filtered, person) => {
|
||||
if (person.workspaceId === workspace.id) {
|
||||
delete person.workspaceId;
|
||||
filtered.push(person);
|
||||
}
|
||||
return filtered;
|
||||
}, []);
|
||||
const pipelineStages = seedTableData.pipelineStages.reduce(
|
||||
(filtered, pipelineStage) => {
|
||||
if (pipelineStage.workspaceId === workspace.id) {
|
||||
delete pipelineStage.workspaceId;
|
||||
filtered.push(pipelineStage);
|
||||
}
|
||||
return filtered;
|
||||
},
|
||||
[],
|
||||
);
|
||||
const pipelines = seedTableData.pipelines.reduce((filtered, pipeline) => {
|
||||
if (pipeline.workspaceId === workspace.id) {
|
||||
delete pipeline.workspaceId;
|
||||
filtered.push(pipeline);
|
||||
}
|
||||
return filtered;
|
||||
}, []);
|
||||
if (
|
||||
isEqual(people, peopleSeed) &&
|
||||
isEqual(companies, companiesSeed) &&
|
||||
isEqual(pipelineStages, formattedPipelineStagesSeed) &&
|
||||
isEqual(pipelines, [pipelinesSeed])
|
||||
) {
|
||||
result[workspace.id].sameAsSeed = true;
|
||||
} else {
|
||||
{
|
||||
result[workspace.id].sameAsSeed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getWorkspaces(options) {
|
||||
const where = options.workspaceId
|
||||
? { id: { equals: options.workspaceId } }
|
||||
: {};
|
||||
return await this.prismaService.client.workspace.findMany({
|
||||
where,
|
||||
orderBy: [{ createdAt: 'asc' }],
|
||||
});
|
||||
}
|
||||
|
||||
async findInactiveWorkspaces(workspaces, result) {
|
||||
const tables = this.getRelevantTables();
|
||||
const maxUpdatedAtForAllWorkspaces =
|
||||
await this.getMaxUpdatedAtForAllWorkspaces(tables, workspaces);
|
||||
const seedTableData = await this.getSeedTableData(workspaces);
|
||||
for (const workspace of workspaces) {
|
||||
for (const table of tables) {
|
||||
await this.addMaxUpdatedAtToWorkspaces(
|
||||
result,
|
||||
workspace,
|
||||
table,
|
||||
maxUpdatedAtForAllWorkspaces,
|
||||
);
|
||||
}
|
||||
await this.detectWorkspacesWithSeedDataOnly(
|
||||
result,
|
||||
workspace,
|
||||
seedTableData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
filterResults(result, options) {
|
||||
for (const workspaceId in result) {
|
||||
const timeDifferenceInSeconds = Math.abs(
|
||||
new Date().getTime() -
|
||||
new Date(result[workspaceId].maxUpdatedAt).getTime(),
|
||||
);
|
||||
const timeDifferenceInDays = Math.ceil(
|
||||
timeDifferenceInSeconds / (1000 * 3600 * 24),
|
||||
);
|
||||
if (
|
||||
timeDifferenceInDays < options.days &&
|
||||
(!result[workspaceId].sameAsSeed ||
|
||||
timeDifferenceInDays < options.sameAsSeedDays)
|
||||
) {
|
||||
delete result[workspaceId];
|
||||
} else {
|
||||
result[workspaceId].inactiveDays = timeDifferenceInDays;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async delete(result, options) {
|
||||
const workspaceCount = Object.keys(result).length;
|
||||
if (workspaceCount) {
|
||||
console.log(
|
||||
`Deleting \x1b[36m${workspaceCount}\x1b[0m inactive since \x1b[36m${options.days} days\x1b[0m or same as seed since \x1b[36m${options.sameAsSeedDays} days\x1b[0m workspaces`,
|
||||
);
|
||||
}
|
||||
let count = 1;
|
||||
for (const workspaceId in result) {
|
||||
process.stdout.write(`- deleting ${workspaceId} ...`);
|
||||
await this.workspaceService.deleteWorkspace({
|
||||
workspaceId,
|
||||
});
|
||||
console.log(
|
||||
` done! ....... ${Math.floor((100 * count) / workspaceCount)}%`,
|
||||
);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
displayResults(result, totalWorkspacesCount) {
|
||||
console.log(result);
|
||||
console.log(
|
||||
`${
|
||||
Object.keys(result).length
|
||||
} out of ${totalWorkspacesCount} workspace(s) checked (${Math.floor(
|
||||
(100 * Object.keys(result).length) / totalWorkspacesCount,
|
||||
)}%) will be deleted`,
|
||||
);
|
||||
}
|
||||
|
||||
async run(
|
||||
_passedParam: string[],
|
||||
options: DataCleanInactiveOptions,
|
||||
): Promise<void> {
|
||||
const result: DataCleanResults = {};
|
||||
const workspaces = await this.getWorkspaces(options);
|
||||
const totalWorkspacesCount = workspaces.length;
|
||||
console.log(totalWorkspacesCount, 'workspace(s) to analyse');
|
||||
await this.findInactiveWorkspaces(workspaces, result);
|
||||
this.filterResults(result, options);
|
||||
this.displayResults(result, totalWorkspacesCount);
|
||||
if (!options.dryRun) {
|
||||
options = await this.inquiererService.ask('confirm', options);
|
||||
if (!options.confirmation) {
|
||||
console.log('Cleaning aborted');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!options.dryRun) {
|
||||
await this.delete(result, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Command, CommandRunner } from 'nest-commander';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { DataSourceService } from 'src/metadata/data-source/data-source.service';
|
||||
import { WorkspaceMigrationService } from 'src/metadata/workspace-migration/workspace-migration.service';
|
||||
@@ -12,6 +13,8 @@ import { seedOpportunity } from 'src/database/typeorm-seeds/workspace/opportunit
|
||||
import { seedPipelineStep } from 'src/database/typeorm-seeds/workspace/pipeline-step';
|
||||
import { seedWorkspaceMember } from 'src/database/typeorm-seeds/workspace/workspaceMember';
|
||||
import { seedPeople } from 'src/database/typeorm-seeds/workspace/people';
|
||||
import { seedCoreSchema } from 'src/database/typeorm-seeds/core';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
// TODO: implement dry-run
|
||||
@Command({
|
||||
@@ -23,6 +26,7 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
||||
workspaceId = '20202020-1c25-4d02-bf25-6aeccf7ea419';
|
||||
|
||||
constructor(
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly dataSourceService: DataSourceService,
|
||||
private readonly typeORMService: TypeORMService,
|
||||
private readonly workspaceMigrationService: WorkspaceMigrationService,
|
||||
@@ -32,6 +36,22 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
try {
|
||||
const dataSource = new DataSource({
|
||||
url: this.environmentService.getPGDatabaseUrl(),
|
||||
type: 'postgres',
|
||||
logging: true,
|
||||
schema: 'public',
|
||||
});
|
||||
await dataSource.initialize();
|
||||
|
||||
await seedCoreSchema(dataSource);
|
||||
await seedMetadataSchema(dataSource);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceMetadata =
|
||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||
this.workspaceId,
|
||||
@@ -46,8 +66,6 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
|
||||
}
|
||||
|
||||
try {
|
||||
await seedMetadataSchema(workspaceDataSource, 'metadata');
|
||||
|
||||
await this.workspaceMigrationService.insertStandardMigrations(
|
||||
this.workspaceId,
|
||||
);
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { DataCleanInactiveCommand } from 'src/database/commands/clean-inactive-workspaces.command';
|
||||
import { ConfirmationQuestion } from 'src/database/commands/questions/confirmation.question';
|
||||
import { PipelineModule } from 'src/core/pipeline/pipeline.module';
|
||||
import { CompanyModule } from 'src/core/company/company.module';
|
||||
import { PersonModule } from 'src/core/person/person.module';
|
||||
import { PrismaModule } from 'src/database/prisma.module';
|
||||
import { WorkspaceManagerModule } from 'src/workspace/workspace-manager/workspace-manager.module';
|
||||
import { DataSourceModule } from 'src/metadata/data-source/data-source.module';
|
||||
import { WorkspaceMigrationModule } from 'src/metadata/workspace-migration/workspace-migration.module';
|
||||
@@ -16,21 +11,13 @@ import { DataSeedWorkspaceCommand } from 'src/database/commands/data-seed-worksp
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
PipelineModule,
|
||||
CompanyModule,
|
||||
PersonModule,
|
||||
WorkspaceManagerModule,
|
||||
PrismaModule,
|
||||
DataSourceModule,
|
||||
TypeORMModule,
|
||||
WorkspaceMigrationModule,
|
||||
WorkspaceMigrationRunnerModule,
|
||||
WorkspaceModule,
|
||||
],
|
||||
providers: [
|
||||
DataSeedWorkspaceCommand,
|
||||
DataCleanInactiveCommand,
|
||||
ConfirmationQuestion,
|
||||
],
|
||||
providers: [DataSeedWorkspaceCommand, ConfirmationQuestion],
|
||||
})
|
||||
export class DatabaseCommandModule {}
|
||||
|
||||
Reference in New Issue
Block a user