Enable roles on api keys (#13334)

This commit is contained in:
nitin
2025-08-03 03:24:10 +05:30
committed by GitHub
parent 2bda929984
commit 4cd2a87833
143 changed files with 3355 additions and 671 deletions
@@ -1,212 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { ApiKey } from 'src/engine/core-modules/api-key/api-key.entity';
import { ApiKeyService } from 'src/engine/core-modules/api-key/api-key.service';
import { Webhook } from 'src/engine/core-modules/webhook/webhook.entity';
import { WebhookService } from 'src/engine/core-modules/webhook/webhook.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { ApiKeyWorkspaceEntity } from 'src/modules/api-key/standard-objects/api-key.workspace-entity';
import { WebhookWorkspaceEntity } from 'src/modules/webhook/standard-objects/webhook.workspace-entity';
@Command({
name: 'upgrade:1-1:migrate-api-keys-webhooks-to-core',
description:
'Migrate API keys and webhooks from workspace schemas to core schema',
})
export class MigrateApiKeysWebhooksToCoreCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(ApiKey, 'core')
private readonly coreApiKeyRepository: Repository<ApiKey>,
@InjectRepository(Webhook, 'core')
private readonly coreWebhookRepository: Repository<Webhook>,
private readonly apiKeyService: ApiKeyService,
private readonly webhookService: WebhookService,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
options,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Migrating API keys and webhooks for workspace ${workspaceId} ${index + 1}/${total}`,
);
try {
await this.migrateApiKeys(workspaceId, options.dryRun);
await this.migrateWebhooks(workspaceId, options.dryRun);
this.logger.log(
`Successfully migrated API keys and webhooks for workspace ${workspaceId}`,
);
} catch (error) {
this.logger.error(
`Failed to migrate API keys and webhooks for workspace ${workspaceId}: ${error.message}`,
);
throw error;
}
}
private async migrateApiKeys(
workspaceId: string,
dryRun?: boolean,
): Promise<void> {
const workspaceApiKeyRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<ApiKeyWorkspaceEntity>(
workspaceId,
'apiKey',
{ shouldBypassPermissionChecks: true },
);
const workspaceApiKeys = await workspaceApiKeyRepository.find({
withDeleted: true,
});
if (workspaceApiKeys.length === 0) {
this.logger.log(`No API keys to migrate for workspace ${workspaceId}`);
return;
}
this.logger.log(
`${dryRun ? 'DRY RUN: ' : ''}Found ${workspaceApiKeys.length} API keys to migrate for workspace ${workspaceId}`,
);
if (dryRun) {
workspaceApiKeys.forEach((apiKey) => {
const deletedStatus = apiKey.deletedAt ? ' (DELETED)' : '';
this.logger.log(
`DRY RUN: Would migrate API key ${apiKey.id} (${apiKey.name})${deletedStatus} from workspace ${workspaceId}`,
);
});
return;
}
const existingCoreApiKeys = await this.coreApiKeyRepository.find({
where: { workspaceId },
select: ['id'],
withDeleted: true,
});
const existingApiKeyIds = new Set(existingCoreApiKeys.map((ak) => ak.id));
for (const workspaceApiKey of workspaceApiKeys) {
if (existingApiKeyIds.has(workspaceApiKey.id)) {
this.logger.warn(
`API key ${workspaceApiKey.id} already exists in core schema for workspace ${workspaceId}, skipping`,
);
continue;
}
await this.apiKeyService.create({
id: workspaceApiKey.id,
name: workspaceApiKey.name,
expiresAt: workspaceApiKey.expiresAt,
revokedAt: workspaceApiKey.revokedAt
? new Date(workspaceApiKey.revokedAt)
: workspaceApiKey.deletedAt
? new Date(workspaceApiKey.deletedAt)
: undefined,
workspaceId,
createdAt: new Date(workspaceApiKey.createdAt),
updatedAt: new Date(workspaceApiKey.updatedAt),
});
const deletedStatus = workspaceApiKey.deletedAt ? ' (DELETED)' : '';
this.logger.log(
`Migrated API key ${workspaceApiKey.id} (${workspaceApiKey.name})${deletedStatus} to core schema`,
);
}
}
private async migrateWebhooks(
workspaceId: string,
dryRun?: boolean,
): Promise<void> {
const workspaceWebhookRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WebhookWorkspaceEntity>(
workspaceId,
'webhook',
{ shouldBypassPermissionChecks: true },
);
const workspaceWebhooks = await workspaceWebhookRepository.find({
withDeleted: true,
});
if (workspaceWebhooks.length === 0) {
this.logger.log(`No webhooks to migrate for workspace ${workspaceId}`);
return;
}
this.logger.log(
`${dryRun ? 'DRY RUN: ' : ''}Found ${workspaceWebhooks.length} webhooks to migrate for workspace ${workspaceId}`,
);
if (dryRun) {
workspaceWebhooks.forEach((webhook) => {
const deletedStatus = webhook.deletedAt ? ' (DELETED)' : '';
this.logger.log(
`DRY RUN: Would migrate webhook ${webhook.id} (${webhook.targetUrl})${deletedStatus} from workspace ${workspaceId}`,
);
});
return;
}
const existingCoreWebhooks = await this.coreWebhookRepository.find({
where: { workspaceId },
select: ['id'],
withDeleted: true,
});
const existingWebhookIds = new Set(existingCoreWebhooks.map((wh) => wh.id));
for (const workspaceWebhook of workspaceWebhooks) {
if (existingWebhookIds.has(workspaceWebhook.id)) {
this.logger.warn(
`Webhook ${workspaceWebhook.id} already exists in core schema for workspace ${workspaceId}, skipping`,
);
continue;
}
await this.webhookService.create({
id: workspaceWebhook.id,
targetUrl: workspaceWebhook.targetUrl,
operations: workspaceWebhook.operations,
description: workspaceWebhook.description,
secret: workspaceWebhook.secret,
workspaceId,
createdAt: new Date(workspaceWebhook.createdAt),
updatedAt: new Date(workspaceWebhook.updatedAt),
deletedAt: workspaceWebhook.deletedAt
? new Date(workspaceWebhook.deletedAt)
: undefined,
});
const deletedStatus = workspaceWebhook.deletedAt ? ' (DELETED)' : '';
this.logger.log(
`Migrated webhook ${workspaceWebhook.id} (${workspaceWebhook.targetUrl})${deletedStatus} to core schema`,
);
}
}
}
@@ -4,7 +4,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { AddEnqueuedStatusToWorkflowRunCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-add-enqueued-status-to-workflow-run.command';
import { FixSchemaArrayTypeCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-schema-array-type.command';
import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command';
import { MigrateApiKeysWebhooksToCoreCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-migrate-api-keys-webhooks-to-core.command';
import { MigrateWorkflowRunStatesCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-migrate-workflow-run-state.command';
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
import { ApiKey } from 'src/engine/core-modules/api-key/api-key.entity';
@@ -49,14 +48,12 @@ import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/wor
FixUpdateStandardFieldsIsLabelSyncedWithName,
FixSchemaArrayTypeCommand,
MigrateWorkflowRunStatesCommand,
MigrateApiKeysWebhooksToCoreCommand,
AddEnqueuedStatusToWorkflowRunCommand,
],
exports: [
FixUpdateStandardFieldsIsLabelSyncedWithName,
FixSchemaArrayTypeCommand,
MigrateWorkflowRunStatesCommand,
MigrateApiKeysWebhooksToCoreCommand,
AddEnqueuedStatusToWorkflowRunCommand,
],
})
@@ -19,13 +19,13 @@ import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/works
],
providers: [
RemoveWorkflowRunsWithoutState,
AddNextStepIdsToWorkflowVersionTriggers,
AddEnqueuedStatusToWorkflowRunV2Command,
AddNextStepIdsToWorkflowVersionTriggers,
],
exports: [
RemoveWorkflowRunsWithoutState,
AddNextStepIdsToWorkflowVersionTriggers,
AddEnqueuedStatusToWorkflowRunV2Command,
AddNextStepIdsToWorkflowVersionTriggers,
],
})
export class V1_2_UpgradeVersionCommandModule {}
@@ -0,0 +1,349 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, In, QueryRunner, Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { ApiKey } from 'src/engine/core-modules/api-key/api-key.entity';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { ADMIN_ROLE_LABEL } from 'src/engine/metadata-modules/permissions/constants/admin-role-label.constants';
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { WorkspaceFeatureFlagsMapCacheService } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.service';
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@Command({
name: 'upgrade:1-3:assign-roles-to-existing-api-keys',
description:
'Assign Admin roles to existing API keys that lack role assignments. ' +
'This ensures existing integrations continue to work after enabling role-based permissions.',
})
export class AssignRolesToExistingApiKeysCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@InjectRepository(ApiKey, 'core')
private readonly apiKeyRepository: Repository<ApiKey>,
@InjectRepository(RoleEntity, 'core')
private readonly roleRepository: Repository<RoleEntity>,
@InjectRepository(RoleTargetsEntity, 'core')
private readonly roleTargetsRepository: Repository<RoleTargetsEntity>,
private readonly featureFlagService: FeatureFlagService,
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
private readonly workspaceFeatureFlagsMapCacheService: WorkspaceFeatureFlagsMapCacheService,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectDataSource('core')
private readonly dataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
index,
total,
workspaceId,
options,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Assigning roles to existing API keys for workspace ${workspaceId} ${index + 1}/${total}`,
);
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
try {
await queryRunner.startTransaction();
try {
const result = await this.assignRolesToWorkspaceApiKeys(
workspaceId,
options.dryRun ?? false,
queryRunner,
);
if (result.failed.length > 0) {
this.logger.warn(
`Workspace ${workspaceId}: Processed ${result.processed}, Assigned roles to ${result.assigned} API keys, Failed: ${result.failed.length}`,
);
this.logger.warn(
`Failed API keys: ${result.failed.map((f) => `${f.name} (${f.id}): ${f.error}`).join(', ')}`,
);
throw new Error(
`Failed to assign roles to ${result.failed.length} API keys`,
);
}
this.logger.log(
`Workspace ${workspaceId}: Processed ${result.processed}, Assigned roles to ${result.assigned} API keys`,
);
if (options.dryRun) {
this.logger.log(
`DRY RUN: Would enable IS_API_KEY_ROLES_ENABLED feature flag for workspace ${workspaceId}`,
);
} else {
const shouldEnableFeatureFlagAndRecomputeCache =
result.assigned > 0 ||
(await this.shouldEnableForZeroApiKeys(workspaceId));
if (shouldEnableFeatureFlagAndRecomputeCache) {
await this.enableApiKeyRolesFeatureFlagWithTransaction(
workspaceId,
result,
queryRunner,
);
} else {
this.logger.log(
`All API keys already have roles and feature flag IS_API_KEY_ROLES_ENABLED already enabled for workspace ${workspaceId}, no action needed`,
);
}
await queryRunner.commitTransaction();
if (shouldEnableFeatureFlagAndRecomputeCache) {
try {
await this.workspacePermissionsCacheService.recomputeApiKeyRoleMapCache(
{
workspaceId,
},
);
await this.workspaceFeatureFlagsMapCacheService.recomputeFeatureFlagsMapCache(
{
workspaceId,
},
);
this.logger.log(
`Recomputed API key role cache and feature flag cache for workspace ${workspaceId}`,
);
} catch (error) {
this.logger.warn(
`Failed to recompute API key role cache and feature flag cache for workspace ${workspaceId}: ${error.message}`,
);
}
}
}
} catch (error) {
if (queryRunner.isTransactionActive) {
await queryRunner.rollbackTransaction();
this.logger.error(
`Transaction rolled back for workspace ${workspaceId} due to error: ${error.message}`,
);
}
throw error;
}
} catch (error) {
this.logger.error(
`Failed to assign roles to existing API keys for workspace ${workspaceId}: ${error.message}`,
);
throw error;
} finally {
await queryRunner.release();
}
}
private async assignRolesToWorkspaceApiKeys(
workspaceId: string,
dryRun: boolean,
queryRunner: QueryRunner,
): Promise<{
processed: number;
assigned: number;
failed: Array<{ id: string; name: string; error: string }>;
}> {
const apiKeys = await this.apiKeyRepository.find({
where: { workspaceId },
select: ['id', 'name', 'workspaceId'],
});
if (apiKeys.length === 0) {
this.logger.log(`No API keys found in workspace ${workspaceId}`);
return { processed: 0, assigned: 0, failed: [] };
}
this.logger.log(
`Found ${apiKeys.length} API keys in workspace ${workspaceId}`,
);
const apiKeyIds = apiKeys.map((key) => key.id);
const existingRoleTargets = await this.roleTargetsRepository.find({
where: {
workspaceId,
apiKeyId: In(apiKeyIds),
},
});
const apiKeysWithRoles = new Set(
existingRoleTargets.map((rt) => rt.apiKeyId),
);
const apiKeysWithoutRoles = apiKeys.filter(
(key) => !apiKeysWithRoles.has(key.id),
);
if (apiKeysWithoutRoles.length === 0) {
this.logger.log(
`All API keys already have role assignments for workspace ${workspaceId}`,
);
return { processed: apiKeys.length, assigned: 0, failed: [] };
}
this.logger.log(
`${dryRun ? 'DRY RUN: ' : ''}Found ${apiKeysWithoutRoles.length} API keys without role assignments for workspace ${workspaceId}`,
);
if (dryRun) {
this.logger.log(
`DRY RUN: Would assign Admin roles to ${apiKeysWithoutRoles.length} API keys for workspace ${workspaceId}`,
);
return {
processed: apiKeys.length,
assigned: apiKeysWithoutRoles.length,
failed: [],
};
}
const adminRole = await this.roleRepository.findOne({
where: {
workspaceId,
label: ADMIN_ROLE_LABEL,
},
});
if (!adminRole) {
throw new Error(
`No Admin role found in workspace ${workspaceId}. Should not happen.`,
);
}
this.logger.log(
`Using Admin role ${adminRole.id} for workspace ${workspaceId}`,
);
try {
let assignedCount = 0;
for (const apiKey of apiKeysWithoutRoles) {
await queryRunner.manager.delete(RoleTargetsEntity, {
apiKeyId: apiKey.id,
workspaceId,
});
const roleTarget = queryRunner.manager.create(RoleTargetsEntity, {
apiKeyId: apiKey.id,
roleId: adminRole.id,
workspaceId,
});
await queryRunner.manager.save(roleTarget);
this.logger.log(
`Assigned Admin role to API key "${apiKey.name}" (${apiKey.id})`,
);
assignedCount++;
}
this.logger.log(
`Successfully assigned roles to ${assignedCount} API keys for workspace ${workspaceId}`,
);
return {
processed: apiKeys.length,
assigned: assignedCount,
failed: [],
};
} catch (error) {
this.logger.error(
`Failed to assign roles to API keys for workspace ${workspaceId}: ${error.message}`,
);
const errorMessage =
error instanceof Error ? error.message : String(error);
const failedApiKeys = apiKeysWithoutRoles.map((apiKey) => ({
id: apiKey.id,
name: apiKey.name,
error: errorMessage,
}));
return {
processed: apiKeys.length,
assigned: 0,
failed: failedApiKeys,
};
}
}
private async enableApiKeyRolesFeatureFlagWithTransaction(
workspaceId: string,
result: { processed: number; assigned: number },
queryRunner: QueryRunner,
): Promise<void> {
const shouldEnableFeatureFlag =
result.processed > 0 ||
(await this.shouldEnableForZeroApiKeys(workspaceId));
if (shouldEnableFeatureFlag) {
try {
const existingFeatureFlag = await queryRunner.manager.findOne(
FeatureFlag,
{
where: {
key: FeatureFlagKey.IS_API_KEY_ROLES_ENABLED,
workspaceId: workspaceId,
},
},
);
const featureFlagToSave = existingFeatureFlag
? {
...existingFeatureFlag,
value: true,
}
: {
key: FeatureFlagKey.IS_API_KEY_ROLES_ENABLED,
value: true,
workspaceId: workspaceId,
};
await queryRunner.manager.save(FeatureFlag, featureFlagToSave);
this.logger.log(
`Enabled IS_API_KEY_ROLES_ENABLED feature flag for workspace ${workspaceId}`,
);
} catch (error) {
this.logger.error(
`Failed to enable feature flag for workspace ${workspaceId}: ${error.message}`,
);
throw new Error(
`Failed to enable API key roles feature flag for workspace ${workspaceId}: ${error.message}`,
);
}
} else {
this.logger.log(
`Feature flag already enabled for workspace ${workspaceId}`,
);
}
}
private async shouldEnableForZeroApiKeys(
workspaceId: string,
): Promise<boolean> {
const isAlreadyEnabled = await this.featureFlagService.isFeatureEnabled(
FeatureFlagKey.IS_API_KEY_ROLES_ENABLED,
workspaceId,
);
return !isAlreadyEnabled;
}
}
@@ -1,8 +1,41 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AssignRolesToExistingApiKeysCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-assign-roles-to-existing-api-keys.command';
import { ApiKey } from 'src/engine/core-modules/api-key/api-key.entity';
import { ApiKeyModule } from 'src/engine/core-modules/api-key/api-key.module';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RoleTargetsEntity } from 'src/engine/metadata-modules/role/role-targets.entity';
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
import { WorkspaceFeatureFlagsMapCacheModule } from 'src/engine/metadata-modules/workspace-feature-flags-map-cache/workspace-feature-flags-map-cache.module';
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
@Module({
imports: [],
providers: [],
exports: [],
imports: [
TypeOrmModule.forFeature(
[
Workspace,
ApiKey,
FieldMetadataEntity,
ObjectMetadataEntity,
RoleEntity,
RoleTargetsEntity,
],
'core',
),
ApiKeyModule,
FeatureFlagModule,
TwentyORMModule,
RoleModule,
WorkspacePermissionsCacheModule,
WorkspaceFeatureFlagsMapCacheModule,
],
providers: [AssignRolesToExistingApiKeysCommand],
exports: [AssignRolesToExistingApiKeysCommand],
})
export class V1_3_UpgradeVersionCommandModule {}
@@ -24,11 +24,11 @@ import { DeduplicateIndexedFieldsCommand } from 'src/database/commands/upgrade-v
import { AddEnqueuedStatusToWorkflowRunCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-add-enqueued-status-to-workflow-run.command';
import { FixSchemaArrayTypeCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-schema-array-type.command';
import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command';
import { MigrateApiKeysWebhooksToCoreCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-migrate-api-keys-webhooks-to-core.command';
import { MigrateWorkflowRunStatesCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-migrate-workflow-run-state.command';
import { AddEnqueuedStatusToWorkflowRunV2Command } from 'src/database/commands/upgrade-version-command/1-2/1-2-add-enqueued-status-to-workflow-run-v2.command';
import { AddNextStepIdsToWorkflowVersionTriggers } from 'src/database/commands/upgrade-version-command/1-2/1-2-add-next-step-ids-to-workflow-version-triggers.command';
import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-version-command/1-2/1-2-remove-workflow-runs-without-state.command';
import { AssignRolesToExistingApiKeysCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-assign-roles-to-existing-api-keys.command';
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@@ -147,7 +147,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
// 1.1 Commands
protected readonly fixSchemaArrayTypeCommand: FixSchemaArrayTypeCommand,
protected readonly fixUpdateStandardFieldsIsLabelSyncedWithNameCommand: FixUpdateStandardFieldsIsLabelSyncedWithName,
protected readonly migrateApiKeysWebhooksToCoreCommand: MigrateApiKeysWebhooksToCoreCommand,
protected readonly migrateWorkflowRunStatesCommand: MigrateWorkflowRunStatesCommand,
protected readonly addEnqueuedStatusToWorkflowRunCommand: AddEnqueuedStatusToWorkflowRunCommand,
@@ -157,6 +156,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
protected readonly addEnqueuedStatusToWorkflowRunV2Command: AddEnqueuedStatusToWorkflowRunV2Command,
// 1.3 Commands
protected readonly assignRolesToExistingApiKeysCommand: AssignRolesToExistingApiKeysCommand,
) {
super(
workspaceRepository,
@@ -201,7 +201,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
beforeSyncMetadata: [
this.fixUpdateStandardFieldsIsLabelSyncedWithNameCommand,
this.fixSchemaArrayTypeCommand,
this.migrateApiKeysWebhooksToCoreCommand,
this.addEnqueuedStatusToWorkflowRunCommand,
],
afterSyncMetadata: [this.migrateWorkflowRunStatesCommand],
@@ -213,7 +212,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
this.addNextStepIdsToWorkflowVersionTriggers,
this.addEnqueuedStatusToWorkflowRunV2Command,
],
afterSyncMetadata: [],
afterSyncMetadata: [this.assignRolesToExistingApiKeysCommand],
};
const commands_130: VersionCommands = {