Add option to synchronize all active workspaces at once (#6221)
In the longer term, we want to improve the efficiency and reliability of the sync-metadata command, by choosing an error handling strategy and paying greater attention to health checks. In the meantime, this PR adds an option to run the sync-metadata command on all active workspaces at once. --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -7,19 +7,11 @@ import { Command, CommandRunner, Option } from 'nest-commander';
|
|||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
|
||||||
import {
|
|
||||||
BillingSubscription,
|
|
||||||
SubscriptionStatus,
|
|
||||||
} from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
|
||||||
import {
|
|
||||||
FeatureFlagEntity,
|
|
||||||
FeatureFlagKeys,
|
|
||||||
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
|
||||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
|
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
|
||||||
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
|
||||||
|
import { WorkspaceStatusService } from 'src/engine/workspace-manager/workspace-status/services/workspace-status.service';
|
||||||
import { COMPANY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
import { COMPANY_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
|
||||||
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
import { ViewFieldWorkspaceEntity } from 'src/modules/view/standard-objects/view-field.workspace-entity';
|
||||||
|
|
||||||
@ -36,18 +28,13 @@ export class AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand extends
|
|||||||
AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand.name,
|
AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand.name,
|
||||||
);
|
);
|
||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(Workspace, 'core')
|
|
||||||
private readonly workspaceRepository: Repository<Workspace>,
|
|
||||||
@InjectRepository(BillingSubscription, 'core')
|
|
||||||
private readonly billingSubscriptionRepository: Repository<BillingSubscription>,
|
|
||||||
@InjectRepository(FeatureFlagEntity, 'core')
|
|
||||||
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
|
||||||
@InjectRepository(FieldMetadataEntity, 'metadata')
|
@InjectRepository(FieldMetadataEntity, 'metadata')
|
||||||
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
|
||||||
private readonly typeORMService: TypeORMService,
|
private readonly typeORMService: TypeORMService,
|
||||||
private readonly dataSourceService: DataSourceService,
|
private readonly dataSourceService: DataSourceService,
|
||||||
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
|
private readonly workspaceCacheVersionService: WorkspaceCacheVersionService,
|
||||||
private readonly twentyORMManager: TwentyORMManager,
|
private readonly twentyORMManager: TwentyORMManager,
|
||||||
|
private readonly workspaceStatusService: WorkspaceStatusService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -76,19 +63,8 @@ export class AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand extends
|
|||||||
if (options.workspaceId) {
|
if (options.workspaceId) {
|
||||||
workspaceIds = [options.workspaceId];
|
workspaceIds = [options.workspaceId];
|
||||||
} else {
|
} else {
|
||||||
const workspaces = await this.workspaceRepository.find();
|
const activeWorkspaceIds =
|
||||||
|
await this.workspaceStatusService.getActiveWorkspaceIds();
|
||||||
const activeWorkspaceIds = (
|
|
||||||
await Promise.all(
|
|
||||||
workspaces.map(async (workspace) => {
|
|
||||||
const isActive = await this.workspaceIsActive(workspace);
|
|
||||||
|
|
||||||
return { workspace, isActive };
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.filter((result) => result.isActive)
|
|
||||||
.map((result) => result.workspace.id);
|
|
||||||
|
|
||||||
workspaceIds = activeWorkspaceIds;
|
workspaceIds = activeWorkspaceIds;
|
||||||
}
|
}
|
||||||
@ -198,33 +174,4 @@ export class AddNewAddressFieldToViewsWithDeprecatedAddressFieldCommand extends
|
|||||||
|
|
||||||
this.logger.log(chalk.green(`Command completed!`));
|
this.logger.log(chalk.green(`Command completed!`));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async workspaceIsActive(workspace: Workspace): Promise<boolean> {
|
|
||||||
const billingSupscriptionForWorkspace =
|
|
||||||
await this.billingSubscriptionRepository.findOne({
|
|
||||||
where: { workspaceId: workspace.id },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
billingSupscriptionForWorkspace?.status &&
|
|
||||||
[
|
|
||||||
SubscriptionStatus.PastDue,
|
|
||||||
SubscriptionStatus.Active,
|
|
||||||
SubscriptionStatus.Trialing,
|
|
||||||
].includes(billingSupscriptionForWorkspace.status as SubscriptionStatus)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const freeAccessEnabledFeatureFlagForWorkspace =
|
|
||||||
await this.featureFlagRepository.findOne({
|
|
||||||
where: {
|
|
||||||
workspaceId: workspace.id,
|
|
||||||
key: FeatureFlagKeys.IsFreeAccessEnabled,
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return !!freeAccessEnabledFeatureFlagForWorkspace;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadat
|
|||||||
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
|
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
|
||||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||||
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
||||||
|
import { WorkspaceStatusModule } from 'src/engine/workspace-manager/workspace-status/workspace-manager.module';
|
||||||
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
|
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@ -42,10 +43,10 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp
|
|||||||
WorkspaceModule,
|
WorkspaceModule,
|
||||||
WorkspaceDataSourceModule,
|
WorkspaceDataSourceModule,
|
||||||
WorkspaceSyncMetadataModule,
|
WorkspaceSyncMetadataModule,
|
||||||
|
WorkspaceStatusModule,
|
||||||
ObjectMetadataModule,
|
ObjectMetadataModule,
|
||||||
DataSeedDemoWorkspaceModule,
|
DataSeedDemoWorkspaceModule,
|
||||||
WorkspaceCacheVersionModule,
|
WorkspaceCacheVersionModule,
|
||||||
|
|
||||||
// Upgrades
|
// Upgrades
|
||||||
UpgradeTo0_22CommandModule,
|
UpgradeTo0_22CommandModule,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { SendInviteLinkEmail } from 'twenty-emails';
|
|
||||||
import { render } from '@react-email/render';
|
import { render } from '@react-email/render';
|
||||||
|
import { SendInviteLinkEmail } from 'twenty-emails';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
|
import { BillingWorkspaceService } from 'src/engine/core-modules/billing/billing.workspace-service';
|
||||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
|
||||||
import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input';
|
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
|
||||||
import { BillingWorkspaceService } from 'src/engine/core-modules/billing/billing.workspace-service';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
|
import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input';
|
||||||
import { SendInviteLink } from 'src/engine/core-modules/workspace/dtos/send-invite-link.entity';
|
import { SendInviteLink } from 'src/engine/core-modules/workspace/dtos/send-invite-link.entity';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { EmailService } from 'src/engine/integrations/email/email.service';
|
import { EmailService } from 'src/engine/integrations/email/email.service';
|
||||||
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||||
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
|
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
|
||||||
|
|
||||||
export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@ -3,20 +3,20 @@ import { Module } from '@nestjs/common';
|
|||||||
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
|
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
|
||||||
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-query-typeorm';
|
||||||
|
|
||||||
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
|
||||||
import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.resolver';
|
|
||||||
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
import { TypeORMModule } from 'src/database/typeorm/typeorm.module';
|
||||||
|
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
|
||||||
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
|
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
|
||||||
|
import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module';
|
||||||
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||||
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module';
|
||||||
import { BillingModule } from 'src/engine/core-modules/billing/billing.module';
|
|
||||||
import { FileUploadModule } from 'src/engine/core-modules/file/file-upload/file-upload.module';
|
|
||||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
|
||||||
import { WorkspaceWorkspaceMemberListener } from 'src/engine/core-modules/workspace/workspace-workspace-member.listener';
|
|
||||||
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
|
|
||||||
import { User } from 'src/engine/core-modules/user/user.entity';
|
|
||||||
import { UserWorkspaceResolver } from 'src/engine/core-modules/user-workspace/user-workspace.resolver';
|
import { UserWorkspaceResolver } from 'src/engine/core-modules/user-workspace/user-workspace.resolver';
|
||||||
import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module';
|
import { User } from 'src/engine/core-modules/user/user.entity';
|
||||||
|
import { WorkspaceWorkspaceMemberListener } from 'src/engine/core-modules/workspace/workspace-workspace-member.listener';
|
||||||
|
import { WorkspaceResolver } from 'src/engine/core-modules/workspace/workspace.resolver';
|
||||||
|
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||||
|
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
|
||||||
|
import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module';
|
||||||
|
|
||||||
import { workspaceAutoResolverOpts } from './workspace.auto-resolver-opts';
|
import { workspaceAutoResolverOpts } from './workspace.auto-resolver-opts';
|
||||||
import { Workspace } from './workspace.entity';
|
import { Workspace } from './workspace.entity';
|
||||||
|
|||||||
@ -4,8 +4,9 @@ import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-s
|
|||||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||||
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
|
||||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||||
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
|
|
||||||
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
|
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
|
||||||
|
import { WorkspaceStatusModule } from 'src/engine/workspace-manager/workspace-status/workspace-manager.module';
|
||||||
|
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
|
||||||
|
|
||||||
import { WorkspaceManagerService } from './workspace-manager.service';
|
import { WorkspaceManagerService } from './workspace-manager.service';
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ import { WorkspaceManagerService } from './workspace-manager.service';
|
|||||||
DataSourceModule,
|
DataSourceModule,
|
||||||
WorkspaceSyncMetadataModule,
|
WorkspaceSyncMetadataModule,
|
||||||
WorkspaceHealthModule,
|
WorkspaceHealthModule,
|
||||||
|
WorkspaceStatusModule,
|
||||||
],
|
],
|
||||||
exports: [WorkspaceManagerService],
|
exports: [WorkspaceManagerService],
|
||||||
providers: [WorkspaceManagerService],
|
providers: [WorkspaceManagerService],
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
||||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service';
|
||||||
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||||
import { standardObjectsPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data';
|
|
||||||
import { demoObjectsPrefillData } from 'src/engine/workspace-manager/demo-objects-prefill-data/demo-objects-prefill-data';
|
|
||||||
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
|
||||||
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
|
import { demoObjectsPrefillData } from 'src/engine/workspace-manager/demo-objects-prefill-data/demo-objects-prefill-data';
|
||||||
|
import { standardObjectsPrefillData } from 'src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data';
|
||||||
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@ -0,0 +1,74 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { Any, Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import {
|
||||||
|
BillingSubscription,
|
||||||
|
SubscriptionStatus,
|
||||||
|
} from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||||
|
import {
|
||||||
|
FeatureFlagEntity,
|
||||||
|
FeatureFlagKeys,
|
||||||
|
} from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { EnvironmentService } from 'src/engine/integrations/environment/environment.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WorkspaceStatusService {
|
||||||
|
constructor(
|
||||||
|
private readonly environmentService: EnvironmentService,
|
||||||
|
@InjectRepository(Workspace, 'core')
|
||||||
|
private readonly workspaceRepository: Repository<Workspace>,
|
||||||
|
@InjectRepository(BillingSubscription, 'core')
|
||||||
|
private readonly billingSubscriptionRepository: Repository<BillingSubscription>,
|
||||||
|
@InjectRepository(FeatureFlagEntity, 'core')
|
||||||
|
private readonly featureFlagRepository: Repository<FeatureFlagEntity>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async getActiveWorkspaceIds(): Promise<string[]> {
|
||||||
|
const workspaces = await this.workspaceRepository.find();
|
||||||
|
const workspaceIds = workspaces.map((workspace) => workspace.id);
|
||||||
|
|
||||||
|
if (!this.environmentService.get('IS_BILLING_ENABLED')) {
|
||||||
|
return workspaceIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
const billingSubscriptionForWorkspaces =
|
||||||
|
await this.billingSubscriptionRepository.find({
|
||||||
|
where: {
|
||||||
|
workspaceId: Any(workspaceIds),
|
||||||
|
status: Any([
|
||||||
|
SubscriptionStatus.PastDue,
|
||||||
|
SubscriptionStatus.Active,
|
||||||
|
SubscriptionStatus.Trialing,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const workspaceIdsWithActiveSubscription =
|
||||||
|
billingSubscriptionForWorkspaces.map(
|
||||||
|
(billingSubscription) => billingSubscription.workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const freeAccessEnabledFeatureFlagForWorkspace =
|
||||||
|
await this.featureFlagRepository.find({
|
||||||
|
where: {
|
||||||
|
workspaceId: Any(workspaceIds),
|
||||||
|
key: FeatureFlagKeys.IsFreeAccessEnabled,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const workspaceIdsWithFreeAccessEnabled =
|
||||||
|
freeAccessEnabledFeatureFlagForWorkspace.map(
|
||||||
|
(featureFlag) => featureFlag.workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return workspaceIds.filter(
|
||||||
|
(workspaceId) =>
|
||||||
|
workspaceIdsWithActiveSubscription.includes(workspaceId) ||
|
||||||
|
workspaceIdsWithFreeAccessEnabled.includes(workspaceId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||||
|
import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
|
||||||
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
|
import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module';
|
||||||
|
import { WorkspaceStatusService } from 'src/engine/workspace-manager/workspace-status/services/workspace-status.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
EnvironmentModule,
|
||||||
|
TypeOrmModule.forFeature(
|
||||||
|
[Workspace, BillingSubscription, FeatureFlagEntity],
|
||||||
|
'core',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
exports: [WorkspaceStatusService],
|
||||||
|
providers: [WorkspaceStatusService],
|
||||||
|
})
|
||||||
|
export class WorkspaceStatusModule {}
|
||||||
@ -1,18 +1,20 @@
|
|||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
|
|
||||||
|
import isEmpty from 'lodash.isempty';
|
||||||
import { Command, CommandRunner, Option } from 'nest-commander';
|
import { Command, CommandRunner, Option } from 'nest-commander';
|
||||||
|
|
||||||
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
|
||||||
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
|
||||||
import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service';
|
import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service';
|
||||||
|
import { WorkspaceStatusService } from 'src/engine/workspace-manager/workspace-status/services/workspace-status.service';
|
||||||
|
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
||||||
|
|
||||||
import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service';
|
import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service';
|
||||||
|
|
||||||
// TODO: implement dry-run
|
// TODO: implement dry-run
|
||||||
interface RunWorkspaceMigrationsOptions {
|
interface RunWorkspaceMigrationsOptions {
|
||||||
workspaceId?: string;
|
|
||||||
dryRun?: boolean;
|
dryRun?: boolean;
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
|
workspaceId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command({
|
@Command({
|
||||||
@ -27,6 +29,7 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner {
|
|||||||
private readonly workspaceHealthService: WorkspaceHealthService,
|
private readonly workspaceHealthService: WorkspaceHealthService,
|
||||||
private readonly dataSourceService: DataSourceService,
|
private readonly dataSourceService: DataSourceService,
|
||||||
private readonly syncWorkspaceLoggerService: SyncWorkspaceLoggerService,
|
private readonly syncWorkspaceLoggerService: SyncWorkspaceLoggerService,
|
||||||
|
private readonly workspaceStatusService: WorkspaceStatusService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -36,7 +39,19 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner {
|
|||||||
options: RunWorkspaceMigrationsOptions,
|
options: RunWorkspaceMigrationsOptions,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// TODO: re-implement load index from workspaceService, this is breaking the logger
|
// TODO: re-implement load index from workspaceService, this is breaking the logger
|
||||||
const workspaceIds = options.workspaceId ? [options.workspaceId] : [];
|
let workspaceIds = options.workspaceId ? [options.workspaceId] : [];
|
||||||
|
|
||||||
|
if (isEmpty(workspaceIds)) {
|
||||||
|
const activeWorkspaceIds =
|
||||||
|
await this.workspaceStatusService.getActiveWorkspaceIds();
|
||||||
|
|
||||||
|
workspaceIds = activeWorkspaceIds;
|
||||||
|
this.logger.log(
|
||||||
|
`Attempting to sync ${activeWorkspaceIds.length} workspaces.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorsDuringSync: string[] = [];
|
||||||
|
|
||||||
for (const workspaceId of workspaceIds) {
|
for (const workspaceId of workspaceIds) {
|
||||||
try {
|
try {
|
||||||
@ -57,7 +72,7 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner {
|
|||||||
'Please use `workspace:health` command to check issues and fix them before running this command.',
|
'Please use `workspace:health` command to check issues and fix them before running this command.',
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
@ -75,34 +90,52 @@ export class SyncWorkspaceMetadataCommand extends CommandRunner {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataSourceMetadata =
|
try {
|
||||||
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
const dataSourceMetadata =
|
||||||
workspaceId,
|
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
|
||||||
);
|
|
||||||
|
|
||||||
const { storage, workspaceMigrations } =
|
|
||||||
await this.workspaceSyncMetadataService.synchronize(
|
|
||||||
{
|
|
||||||
workspaceId,
|
workspaceId,
|
||||||
dataSourceId: dataSourceMetadata.id,
|
);
|
||||||
},
|
|
||||||
{ applyChanges: !options.dryRun },
|
const { storage, workspaceMigrations } =
|
||||||
|
await this.workspaceSyncMetadataService.synchronize(
|
||||||
|
{
|
||||||
|
workspaceId,
|
||||||
|
dataSourceId: dataSourceMetadata.id,
|
||||||
|
},
|
||||||
|
{ applyChanges: !options.dryRun },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (options.dryRun) {
|
||||||
|
await this.syncWorkspaceLoggerService.saveLogs(
|
||||||
|
workspaceId,
|
||||||
|
storage,
|
||||||
|
workspaceMigrations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
errorsDuringSync.push(
|
||||||
|
`Failed to synchronize workspace ${workspaceId}: ${error.message}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.dryRun) {
|
continue;
|
||||||
await this.syncWorkspaceLoggerService.saveLogs(
|
|
||||||
workspaceId,
|
|
||||||
storage,
|
|
||||||
workspaceMigrations,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.logger.log(
|
||||||
|
`Finished synchronizing all active workspaces (${
|
||||||
|
workspaceIds.length
|
||||||
|
} workspaces). ${
|
||||||
|
errorsDuringSync.length > 0
|
||||||
|
? 'Errors during sync:\n' + errorsDuringSync.join('.\n')
|
||||||
|
: ''
|
||||||
|
}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Option({
|
@Option({
|
||||||
flags: '-w, --workspace-id [workspace_id]',
|
flags: '-w, --workspace-id [workspace_id]',
|
||||||
description: 'workspace id',
|
description: 'workspace id',
|
||||||
required: true,
|
required: false,
|
||||||
})
|
})
|
||||||
parseWorkspaceId(value: string): string {
|
parseWorkspaceId(value: string): string {
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||||
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
|
|
||||||
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
|
|
||||||
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
|
import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module';
|
||||||
|
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
|
||||||
|
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
||||||
|
import { WorkspaceHealthModule } from 'src/engine/workspace-manager/workspace-health/workspace-health.module';
|
||||||
|
import { WorkspaceStatusModule } from 'src/engine/workspace-manager/workspace-status/workspace-manager.module';
|
||||||
import { AddStandardIdCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/add-standard-id.command';
|
import { AddStandardIdCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/add-standard-id.command';
|
||||||
import { ConvertRecordPositionsToIntegers } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command';
|
import { ConvertRecordPositionsToIntegers } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/convert-record-positions-to-integers.command';
|
||||||
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
|
import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.module';
|
||||||
|
|
||||||
import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command';
|
import { SyncWorkspaceMetadataCommand } from './sync-workspace-metadata.command';
|
||||||
|
|
||||||
@ -19,6 +22,8 @@ import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.ser
|
|||||||
WorkspaceModule,
|
WorkspaceModule,
|
||||||
DataSourceModule,
|
DataSourceModule,
|
||||||
WorkspaceDataSourceModule,
|
WorkspaceDataSourceModule,
|
||||||
|
TypeOrmModule.forFeature([Workspace], 'core'),
|
||||||
|
WorkspaceStatusModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
SyncWorkspaceMetadataCommand,
|
SyncWorkspaceMetadataCommand,
|
||||||
|
|||||||
@ -5,18 +5,18 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-
|
|||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||||
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
|
||||||
|
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
|
||||||
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
|
||||||
|
import { WorkspaceMigrationBuilderModule } from 'src/engine/workspace-manager/workspace-migration-builder/workspace-migration-builder.module';
|
||||||
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
|
||||||
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
|
||||||
import { workspaceSyncMetadataFactories } from 'src/engine/workspace-manager/workspace-sync-metadata/factories';
|
|
||||||
import { workspaceSyncMetadataComparators } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators';
|
import { workspaceSyncMetadataComparators } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators';
|
||||||
|
import { workspaceSyncMetadataFactories } from 'src/engine/workspace-manager/workspace-sync-metadata/factories';
|
||||||
import { WorkspaceMetadataUpdaterService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service';
|
import { WorkspaceMetadataUpdaterService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service';
|
||||||
|
import { WorkspaceSyncFieldMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-field-metadata.service';
|
||||||
|
import { WorkspaceSyncIndexMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-index-metadata.service';
|
||||||
import { WorkspaceSyncObjectMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata.service';
|
import { WorkspaceSyncObjectMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-object-metadata.service';
|
||||||
import { WorkspaceSyncRelationMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-relation-metadata.service';
|
import { WorkspaceSyncRelationMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-relation-metadata.service';
|
||||||
import { WorkspaceSyncFieldMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-field-metadata.service';
|
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
|
||||||
import { WorkspaceMigrationBuilderModule } from 'src/engine/workspace-manager/workspace-migration-builder/workspace-migration-builder.module';
|
|
||||||
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
|
|
||||||
import { WorkspaceSyncIndexMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-sync-index-metadata.service';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|||||||
Reference in New Issue
Block a user