Bump current version to 1.23.0 (#19683)
This commit is contained in:
@@ -792,7 +792,6 @@ type Workspace {
|
||||
isCustomDomainEnabled: Boolean!
|
||||
editableProfileFields: [String!]
|
||||
defaultRole: Role
|
||||
version: String
|
||||
fastModel: String!
|
||||
smartModel: String!
|
||||
aiAdditionalInstructions: String
|
||||
|
||||
@@ -579,7 +579,6 @@ export interface Workspace {
|
||||
isCustomDomainEnabled: Scalars['Boolean']
|
||||
editableProfileFields?: Scalars['String'][]
|
||||
defaultRole?: Role
|
||||
version?: Scalars['String']
|
||||
fastModel: Scalars['String']
|
||||
smartModel: Scalars['String']
|
||||
aiAdditionalInstructions?: Scalars['String']
|
||||
@@ -3789,7 +3788,6 @@ export interface WorkspaceGenqlSelection{
|
||||
isCustomDomainEnabled?: boolean | number
|
||||
editableProfileFields?: boolean | number
|
||||
defaultRole?: RoleGenqlSelection
|
||||
version?: boolean | number
|
||||
fastModel?: boolean | number
|
||||
smartModel?: boolean | number
|
||||
aiAdditionalInstructions?: boolean | number
|
||||
|
||||
@@ -1675,9 +1675,6 @@ export default {
|
||||
"defaultRole": [
|
||||
29
|
||||
],
|
||||
"version": [
|
||||
1
|
||||
],
|
||||
"fastModel": [
|
||||
1
|
||||
],
|
||||
|
||||
@@ -6328,7 +6328,6 @@ export type Workspace = {
|
||||
trashRetentionDays: Scalars['Float'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
useRecommendedModels: Scalars['Boolean'];
|
||||
version?: Maybe<Scalars['String']>;
|
||||
viewFields?: Maybe<Array<ViewField>>;
|
||||
viewFilterGroups?: Maybe<Array<ViewFilterGroup>>;
|
||||
viewFilters?: Maybe<Array<ViewFilter>>;
|
||||
|
||||
+14
-14
@@ -26,13 +26,13 @@ export class TestFastInstanceCommand implements FastInstanceCommand {
|
||||
exports[`InstanceCommandGenerationService should escape backslashes in SQL queries 1`] = `
|
||||
{
|
||||
"className": "UpdatePathFastInstanceCommand",
|
||||
"fileName": "1-22-instance-command-fast-1775000000000-update-path.ts",
|
||||
"fileName": "1-23-instance-command-fast-1775000000000-update-path.ts",
|
||||
"fileTemplate": "import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('1.22.0', 1775000000000)
|
||||
@RegisteredInstanceCommand('1.23.0', 1775000000000)
|
||||
export class UpdatePathFastInstanceCommand implements FastInstanceCommand {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('UPDATE "core"."config" SET "value" = E\\'path\\\\\\\\to\\\\\\\\file\\'');
|
||||
@@ -49,13 +49,13 @@ export class UpdatePathFastInstanceCommand implements FastInstanceCommand {
|
||||
exports[`InstanceCommandGenerationService should escape single quotes in SQL queries 1`] = `
|
||||
{
|
||||
"className": "UpdateConfigFastInstanceCommand",
|
||||
"fileName": "1-22-instance-command-fast-1775000000000-update-config.ts",
|
||||
"fileName": "1-23-instance-command-fast-1775000000000-update-config.ts",
|
||||
"fileTemplate": "import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('1.22.0', 1775000000000)
|
||||
@RegisteredInstanceCommand('1.23.0', 1775000000000)
|
||||
export class UpdateConfigFastInstanceCommand implements FastInstanceCommand {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('UPDATE "core"."config" SET "value" = \\'it\\'\\'s done\\'');
|
||||
@@ -72,13 +72,13 @@ export class UpdateConfigFastInstanceCommand implements FastInstanceCommand {
|
||||
exports[`InstanceCommandGenerationService should generate a migration with a single up/down query 1`] = `
|
||||
{
|
||||
"className": "AddFooColumnFastInstanceCommand",
|
||||
"fileName": "1-22-instance-command-fast-1775000000000-add-foo-column.ts",
|
||||
"fileName": "1-23-instance-command-fast-1775000000000-add-foo-column.ts",
|
||||
"fileTemplate": "import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('1.22.0', 1775000000000)
|
||||
@RegisteredInstanceCommand('1.23.0', 1775000000000)
|
||||
export class AddFooColumnFastInstanceCommand implements FastInstanceCommand {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE "core"."user" ADD "foo" varchar');
|
||||
@@ -95,13 +95,13 @@ export class AddFooColumnFastInstanceCommand implements FastInstanceCommand {
|
||||
exports[`InstanceCommandGenerationService should generate a migration with multiple queries 1`] = `
|
||||
{
|
||||
"className": "CreateTaskTableFastInstanceCommand",
|
||||
"fileName": "1-22-instance-command-fast-1775000000000-create-task-table.ts",
|
||||
"fileName": "1-23-instance-command-fast-1775000000000-create-task-table.ts",
|
||||
"fileTemplate": "import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('1.22.0', 1775000000000)
|
||||
@RegisteredInstanceCommand('1.23.0', 1775000000000)
|
||||
export class CreateTaskTableFastInstanceCommand implements FastInstanceCommand {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('CREATE TABLE "core"."task" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" varchar NOT NULL)');
|
||||
@@ -120,13 +120,13 @@ export class CreateTaskTableFastInstanceCommand implements FastInstanceCommand {
|
||||
exports[`InstanceCommandGenerationService should generate a migration with query parameters 1`] = `
|
||||
{
|
||||
"className": "SeedSettingFastInstanceCommand",
|
||||
"fileName": "1-22-instance-command-fast-1775000000000-seed-setting.ts",
|
||||
"fileName": "1-23-instance-command-fast-1775000000000-seed-setting.ts",
|
||||
"fileTemplate": "import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('1.22.0', 1775000000000)
|
||||
@RegisteredInstanceCommand('1.23.0', 1775000000000)
|
||||
export class SeedSettingFastInstanceCommand implements FastInstanceCommand {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('INSERT INTO "core"."setting" ("key", "value") VALUES ($1, $2)', ["theme","dark"]);
|
||||
@@ -143,13 +143,13 @@ export class SeedSettingFastInstanceCommand implements FastInstanceCommand {
|
||||
exports[`InstanceCommandGenerationService should generate a slow instance command with populated up/down 1`] = `
|
||||
{
|
||||
"className": "MakeColumnNotNullableSlowInstanceCommand",
|
||||
"fileName": "1-22-instance-command-slow-1775000000000-make-column-not-nullable.ts",
|
||||
"fileName": "1-23-instance-command-slow-1775000000000-make-column-not-nullable.ts",
|
||||
"fileTemplate": "import { DataSource, QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('1.22.0', 1775000000000, { type: 'slow' })
|
||||
@RegisteredInstanceCommand('1.23.0', 1775000000000, { type: 'slow' })
|
||||
export class MakeColumnNotNullableSlowInstanceCommand implements SlowInstanceCommand {
|
||||
async runDataMigration(dataSource: DataSource): Promise<void> {
|
||||
// TODO: implement data backfill before the DDL migration
|
||||
@@ -170,13 +170,13 @@ export class MakeColumnNotNullableSlowInstanceCommand implements SlowInstanceCom
|
||||
exports[`InstanceCommandGenerationService should use default migration name in class and file names 1`] = `
|
||||
{
|
||||
"className": "AutoGeneratedFastInstanceCommand",
|
||||
"fileName": "1-22-instance-command-fast-1775000000000-auto-generated.ts",
|
||||
"fileName": "1-23-instance-command-fast-1775000000000-auto-generated.ts",
|
||||
"fileTemplate": "import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('1.22.0', 1775000000000)
|
||||
@RegisteredInstanceCommand('1.23.0', 1775000000000)
|
||||
export class AutoGeneratedFastInstanceCommand implements FastInstanceCommand {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE "core"."user" ADD "bar" integer');
|
||||
|
||||
+1
-2
@@ -3,10 +3,9 @@ import { Module } from '@nestjs/common';
|
||||
import { WorkspaceIteratorModule } from 'src/database/commands/command-runners/workspace-iterator.module';
|
||||
import { UpgradeCommand } from 'src/database/commands/upgrade-version-command/upgrade.command';
|
||||
import { UpgradeModule } from 'src/engine/core-modules/upgrade/upgrade.module';
|
||||
import { WorkspaceVersionModule } from 'src/engine/workspace-manager/workspace-version/workspace-version.module';
|
||||
|
||||
@Module({
|
||||
imports: [UpgradeModule, WorkspaceVersionModule, WorkspaceIteratorModule],
|
||||
imports: [UpgradeModule, WorkspaceIteratorModule],
|
||||
providers: [UpgradeCommand],
|
||||
})
|
||||
export class UpgradeVersionCommandModule {}
|
||||
|
||||
+1
-171
@@ -1,20 +1,10 @@
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
import { DataSource, In } from 'typeorm';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { CommandLogger } from 'src/database/commands/logger';
|
||||
import { UpgradeCommandRegistryService } from 'src/engine/core-modules/upgrade/services/upgrade-command-registry.service';
|
||||
import { UpgradeMigrationService } from 'src/engine/core-modules/upgrade/services/upgrade-migration.service';
|
||||
import { UpgradeSequenceReaderService } from 'src/engine/core-modules/upgrade/services/upgrade-sequence-reader.service';
|
||||
import { UpgradeSequenceRunnerService } from 'src/engine/core-modules/upgrade/services/upgrade-sequence-runner.service';
|
||||
import { RemovedSinceVersion } from 'src/engine/core-modules/upgrade/types/removed-since-version.type';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { WorkspaceVersionService } from 'src/engine/workspace-manager/workspace-version/services/workspace-version.service';
|
||||
import { compareVersionMajorAndMinor } from 'src/utils/version/compare-version-minor-and-major';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type RawUpgradeCommandOptions = {
|
||||
workspaceId?: Set<string>;
|
||||
@@ -40,13 +30,8 @@ export class UpgradeCommand extends CommandRunner {
|
||||
protected logger: CommandLogger;
|
||||
|
||||
constructor(
|
||||
protected readonly upgradeCommandRegistryService: UpgradeCommandRegistryService,
|
||||
protected readonly upgradeSequenceReaderService: UpgradeSequenceReaderService,
|
||||
protected readonly upgradeSequenceRunnerService: UpgradeSequenceRunnerService,
|
||||
protected readonly upgradeMigrationService: UpgradeMigrationService,
|
||||
protected readonly workspaceVersionService: WorkspaceVersionService,
|
||||
@InjectDataSource()
|
||||
protected readonly dataSource: DataSource,
|
||||
) {
|
||||
super();
|
||||
this.logger = new CommandLogger({
|
||||
@@ -129,10 +114,6 @@ export class UpgradeCommand extends CommandRunner {
|
||||
}
|
||||
|
||||
try {
|
||||
await this.runBootstrapMigrations();
|
||||
await this.guardAllActiveOrSuspendedWorkspacesAreIn1_21_0();
|
||||
await this.backfillWorkspaceCreatedIn1_21_0Cursors();
|
||||
|
||||
const sequence = this.upgradeSequenceReaderService.getUpgradeSequence();
|
||||
|
||||
this.logger.log(
|
||||
@@ -175,155 +156,4 @@ export class UpgradeCommand extends CommandRunner {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async guardAllActiveOrSuspendedWorkspacesAreIn1_21_0(): RemovedSinceVersion<
|
||||
'1.23.0',
|
||||
Promise<void>
|
||||
> {
|
||||
const MINIMUM_VERSION = '1.21.0';
|
||||
|
||||
const activeOrSuspendedWorkspaces = await this.dataSource
|
||||
.getRepository(WorkspaceEntity)
|
||||
.find({
|
||||
select: {
|
||||
version: true,
|
||||
id: true,
|
||||
},
|
||||
where: {
|
||||
activationStatus: In([
|
||||
WorkspaceActivationStatus.ACTIVE,
|
||||
WorkspaceActivationStatus.SUSPENDED,
|
||||
]),
|
||||
},
|
||||
});
|
||||
|
||||
const workspacesBelowMinimum = activeOrSuspendedWorkspaces.filter(
|
||||
(workspace) =>
|
||||
!isDefined(workspace.version) ||
|
||||
compareVersionMajorAndMinor(workspace.version, MINIMUM_VERSION) ===
|
||||
'lower',
|
||||
);
|
||||
|
||||
if (workspacesBelowMinimum.length > 0) {
|
||||
const listing = workspacesBelowMinimum
|
||||
.map(
|
||||
(workspace) =>
|
||||
` - ${workspace.id} (version: ${workspace.version ?? 'null'})`,
|
||||
)
|
||||
.join('\n');
|
||||
|
||||
throw new Error(
|
||||
`Cannot upgrade: ${workspacesBelowMinimum.length} workspace(s) have a version below ${MINIMUM_VERSION}.\n` +
|
||||
`All workspaces must be upgraded to at least ${MINIMUM_VERSION} before running the 1.22.0 upgrade.\n` +
|
||||
`Affected workspaces:\n${listing}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Workspaces created during 1.21 were activated before the cursor-based
|
||||
// upgrade system existed. They have no upgradeMigration record yet.
|
||||
// Stamp them with the last 1.21 workspace command as their initial cursor.
|
||||
private async backfillWorkspaceCreatedIn1_21_0Cursors(): RemovedSinceVersion<
|
||||
'1.23.0',
|
||||
Promise<void>
|
||||
> {
|
||||
const allWorkspaceIds =
|
||||
await this.workspaceVersionService.getActiveOrSuspendedWorkspaceIds();
|
||||
|
||||
if (allWorkspaceIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingCursorWorkspaceIds: { workspaceId: string }[] =
|
||||
await this.dataSource.query(
|
||||
`SELECT DISTINCT "workspaceId" FROM "core"."upgradeMigration" WHERE "workspaceId" IS NOT NULL`,
|
||||
);
|
||||
|
||||
const existingCursorSet = new Set(
|
||||
existingCursorWorkspaceIds.map((row) => row.workspaceId),
|
||||
);
|
||||
|
||||
const workspacesWithoutCursor = allWorkspaceIds.filter(
|
||||
(workspaceId) => !existingCursorSet.has(workspaceId),
|
||||
);
|
||||
|
||||
if (workspacesWithoutCursor.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastWorkspaceCommand =
|
||||
this.upgradeCommandRegistryService.getLastWorkspaceCommandForVersion(
|
||||
'1.21.0',
|
||||
);
|
||||
|
||||
if (!lastWorkspaceCommand) {
|
||||
throw new Error(
|
||||
`Cannot backfill workspace cursors: no workspace commands found for version 1.21.0`,
|
||||
);
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
chalk.blue(
|
||||
`Backfilling initial cursor for ${workspacesWithoutCursor.length} workspace(s) → "${lastWorkspaceCommand.name}"`,
|
||||
),
|
||||
);
|
||||
|
||||
for (const workspaceId of workspacesWithoutCursor) {
|
||||
await this.upgradeMigrationService.markAsInitial({
|
||||
name: lastWorkspaceCommand.name,
|
||||
workspaceId,
|
||||
executedByVersion: '1.21.0',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Schema changes required by the upgrade engine itself (e.g. new columns
|
||||
// on upgradeMigration) must be applied before the sequence runs.
|
||||
private async runBootstrapMigrations(): RemovedSinceVersion<
|
||||
'1.23.0',
|
||||
Promise<void>
|
||||
> {
|
||||
const BOOTSTRAP_MIGRATION = 'AddIsInitialToUpgradeMigration1775909335324';
|
||||
|
||||
const alreadyExecuted = await this.dataSource.query(
|
||||
`SELECT 1 FROM "core"."_typeorm_migrations" WHERE "name" = $1`,
|
||||
[BOOTSTRAP_MIGRATION],
|
||||
);
|
||||
|
||||
if (alreadyExecuted.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const migration = this.dataSource.migrations.find(
|
||||
(migration) => migration.name === BOOTSTRAP_MIGRATION,
|
||||
);
|
||||
|
||||
if (!migration) {
|
||||
throw new Error(
|
||||
`Bootstrap migration "${BOOTSTRAP_MIGRATION}" not found in registered migrations`,
|
||||
);
|
||||
}
|
||||
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
await migration.up(queryRunner);
|
||||
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "core"."_typeorm_migrations" ("timestamp", "name") VALUES ($1, $2)`,
|
||||
[1775909335324, BOOTSTRAP_MIGRATION],
|
||||
);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export const TWENTY_CURRENT_VERSION = '1.22.0' as const;
|
||||
export const TWENTY_CURRENT_VERSION = '1.23.0' as const;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export const TWENTY_NEXT_VERSIONS = ['1.23.0'] as const;
|
||||
export const TWENTY_NEXT_VERSIONS = [] as const;
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
export const TWENTY_PREVIOUS_VERSIONS = ['1.21.0'] as const;
|
||||
export const TWENTY_PREVIOUS_VERSIONS = ['1.21.0', '1.22.0'] as const;
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`UpgradeSequenceRunnerService — failing sequence (integration) should throw when cursor command is not found in the sequence 1`] = `"Step "RemovedCommand" not found in upgrade sequence. The sequence only covers versions [1.21.0, 1.22.0]. Please upgrade to 1.21.0 first."`;
|
||||
exports[`UpgradeSequenceRunnerService — failing sequence (integration) should throw when cursor command is not found in the sequence 1`] = `"Step "RemovedCommand" not found in upgrade sequence. The sequence only covers versions [1.21.0, 1.22.0, 1.23.0]. Please upgrade to 1.21.0 first."`;
|
||||
|
||||
-1
@@ -39,5 +39,4 @@ export const fromWorkspaceEntityToFlat = (
|
||||
updatedAt: entity.updatedAt.toISOString(),
|
||||
deletedAt: entity.deletedAt?.toISOString(),
|
||||
suspendedAt: entity.suspendedAt?.toISOString() ?? null,
|
||||
version: entity.version ?? null,
|
||||
});
|
||||
|
||||
@@ -32,7 +32,6 @@ import { KeyValuePairEntity } from 'src/engine/core-modules/key-value-pair/key-v
|
||||
import { PostgresCredentialsEntity } from 'src/engine/core-modules/postgres-credentials/postgres-credentials.entity';
|
||||
import { PublicDomainEntity } from 'src/engine/core-modules/public-domain/public-domain.entity';
|
||||
import { WorkspaceSSOIdentityProviderEntity } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
|
||||
import { RemovedSinceVersion } from 'src/engine/core-modules/upgrade/types/removed-since-version.type';
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { type ModelId } from 'src/engine/metadata-modules/ai/ai-models/types/model-id.type';
|
||||
@@ -295,10 +294,6 @@ export class WorkspaceEntity {
|
||||
@Field(() => RoleDTO, { nullable: true })
|
||||
defaultRole: RoleDTO | null;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
version: RemovedSinceVersion<'1.23.0', string | null>;
|
||||
|
||||
@Field(() => String, { nullable: false })
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
|
||||
Reference in New Issue
Block a user