Treat suspended workspace as workspaces that need to be synced (#9669)

In this PR:
- migrate WorkspaceActivationStatus to twenty-shared (and update case to
make FE and BE consistent)
- introduce isWorkspaceActiveOrSuspended in twenty-shared
- refactor the code to use it (when we fetch data on the FE, we want to
keep SUSPENDED workspace working + when we sync workspaces we want it
too)
This commit is contained in:
Charles Bochet
2025-01-16 15:01:04 +01:00
committed by GitHub
parent 4a0b89d094
commit f545bd1c40
41 changed files with 200 additions and 167 deletions

View File

@ -24,7 +24,7 @@ const jestConfig: JestConfigWithTsJest = {
extensionsToTreatAsEsm: ['.ts', '.tsx'], extensionsToTreatAsEsm: ['.ts', '.tsx'],
coverageThreshold: { coverageThreshold: {
global: { global: {
statements: 58, statements: 57,
lines: 55, lines: 55,
functions: 47, functions: 47,
}, },

View File

@ -1784,11 +1784,11 @@ export type WorkspaceBillingSubscriptionsArgs = {
}; };
export enum WorkspaceActivationStatus { export enum WorkspaceActivationStatus {
Active = 'ACTIVE', ACTIVE = 'ACTIVE',
Inactive = 'INACTIVE', INACTIVE = 'INACTIVE',
OngoingCreation = 'ONGOING_CREATION', ONGOING_CREATION = 'ONGOING_CREATION',
PendingCreation = 'PENDING_CREATION', PENDING_CREATION = 'PENDING_CREATION',
Suspended = 'SUSPENDED' SUSPENDED = 'SUSPENDED'
} }
export type WorkspaceEdge = { export type WorkspaceEdge = {

View File

@ -1556,11 +1556,11 @@ export type WorkspaceBillingSubscriptionsArgs = {
}; };
export enum WorkspaceActivationStatus { export enum WorkspaceActivationStatus {
Active = 'ACTIVE', ACTIVE = 'ACTIVE',
Inactive = 'INACTIVE', INACTIVE = 'INACTIVE',
OngoingCreation = 'ONGOING_CREATION', ONGOING_CREATION = 'ONGOING_CREATION',
PendingCreation = 'PENDING_CREATION', PENDING_CREATION = 'PENDING_CREATION',
Suspended = 'SUSPENDED' SUSPENDED = 'SUSPENDED'
} }
export type WorkspaceEdge = { export type WorkspaceEdge = {

View File

@ -85,6 +85,15 @@ const testCases = [
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: undefined },
{ loc: AppPath.VerifyEmail, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
{ loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
{ loc: AppPath.CreateWorkspace, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp }, { loc: AppPath.CreateWorkspace, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },

View File

@ -1,7 +1,7 @@
import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type'; import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
import { import {
BillingPlanKey, BillingPlanKey,
SubscriptionInterval, SubscriptionInterval,
} from '~/generated-metadata/graphql'; } from '~/generated-metadata/graphql';
export const BILLING_CHECKOUT_SESSION_DEFAULT_VALUE: BillingCheckoutSession = { export const BILLING_CHECKOUT_SESSION_DEFAULT_VALUE: BillingCheckoutSession = {

View File

@ -5,7 +5,7 @@ import { currentUserState } from '@/auth/states/currentUserState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useLoadMockedObjectMetadataItems } from '@/object-metadata/hooks/useLoadMockedObjectMetadataItems'; import { useLoadMockedObjectMetadataItems } from '@/object-metadata/hooks/useLoadMockedObjectMetadataItems';
import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem'; import { useRefreshObjectMetadataItems } from '@/object-metadata/hooks/useRefreshObjectMetadataItem';
import { WorkspaceActivationStatus } from '~/generated/graphql'; import { isWorkspaceActiveOrSuspended } from 'twenty-shared';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
export const ObjectMetadataItemsLoadEffect = () => { export const ObjectMetadataItemsLoadEffect = () => {
@ -18,7 +18,7 @@ export const ObjectMetadataItemsLoadEffect = () => {
useEffect(() => { useEffect(() => {
if ( if (
isUndefinedOrNull(currentUser) || isUndefinedOrNull(currentUser) ||
currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active !isWorkspaceActiveOrSuspended(currentWorkspace)
) { ) {
loadMockedObjectMetadataItems(); loadMockedObjectMetadataItems();
} else { } else {
@ -26,7 +26,7 @@ export const ObjectMetadataItemsLoadEffect = () => {
} }
}, [ }, [
currentUser, currentUser,
currentWorkspace?.activationStatus, currentWorkspace,
loadMockedObjectMetadataItems, loadMockedObjectMetadataItems,
refreshObjectMetadataItems, refreshObjectMetadataItems,
]); ]);

View File

@ -16,7 +16,7 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({
featureFlags: [], featureFlags: [],
allowImpersonation: false, allowImpersonation: false,
subdomain: 'test', subdomain: 'test',
activationStatus: WorkspaceActivationStatus.Active, activationStatus: WorkspaceActivationStatus.ACTIVE,
hasValidEntrepriseKey: false, hasValidEntrepriseKey: false,
metadataVersion: 1, metadataVersion: 1,
isPublicInviteLinkEnabled: false, isPublicInviteLinkEnabled: false,

View File

@ -2,7 +2,7 @@ import { useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector'; import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { WorkspaceActivationStatus } from '~/generated/graphql'; import { isWorkspaceActiveOrSuspended } from 'twenty-shared';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
@ -20,7 +20,7 @@ export const useObjectNamePluralFromSingular = ({
}), }),
); );
if (currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active) { if (!isWorkspaceActiveOrSuspended(currentWorkspace)) {
objectMetadataItem = objectMetadataItem =
generatedMockObjectMetadataItems.find( generatedMockObjectMetadataItems.find(
(objectMetadataItem) => (objectMetadataItem) =>

View File

@ -2,7 +2,7 @@ import { useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector'; import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { WorkspaceActivationStatus } from '~/generated/graphql'; import { isWorkspaceActiveOrSuspended } from 'twenty-shared';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
@ -20,7 +20,7 @@ export const useObjectNameSingularFromPlural = ({
}), }),
); );
if (currentWorkspace?.activationStatus !== WorkspaceActivationStatus.Active) { if (!isWorkspaceActiveOrSuspended(currentWorkspace)) {
objectMetadataItem = objectMetadataItem =
generatedMockObjectMetadataItems.find( generatedMockObjectMetadataItems.find(
(objectMetadataItem) => (objectMetadataItem) =>

View File

@ -7,7 +7,7 @@ import { useFindManyRecordIndexTableParams } from '@/object-record/record-index/
import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields'; import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies'; import { SIGN_IN_BACKGROUND_MOCK_COMPANIES } from '@/sign-in-background-mock/constants/SignInBackgroundMockCompanies';
import { WorkspaceActivationStatus } from '~/generated/graphql'; import { isWorkspaceActiveOrSuspended } from 'twenty-shared';
export const useLazyLoadRecordIndexTable = (objectNameSingular: string) => { export const useLazyLoadRecordIndexTable = (objectNameSingular: string) => {
const { objectMetadataItem } = useObjectMetadataItem({ const { objectMetadataItem } = useObjectMetadataItem({
@ -44,10 +44,9 @@ export const useLazyLoadRecordIndexTable = (objectNameSingular: string) => {
return { return {
findManyRecords, findManyRecords,
records: records: isWorkspaceActiveOrSuspended(currentWorkspace)
currentWorkspace?.activationStatus === WorkspaceActivationStatus.Active ? records
? records : SIGN_IN_BACKGROUND_MOCK_COMPANIES,
: SIGN_IN_BACKGROUND_MOCK_COMPANIES,
totalCount: totalCount, totalCount: totalCount,
loading, loading,
fetchMoreRecords, fetchMoreRecords,

View File

@ -16,7 +16,7 @@ import {
const currentWorkspace = { const currentWorkspace = {
id: '1', id: '1',
currentBillingSubscription: { status: SubscriptionStatus.Incomplete }, currentBillingSubscription: { status: SubscriptionStatus.Incomplete },
activationStatus: WorkspaceActivationStatus.Active, activationStatus: WorkspaceActivationStatus.ACTIVE,
allowImpersonation: true, allowImpersonation: true,
} as CurrentWorkspace; } as CurrentWorkspace;

View File

@ -6,6 +6,6 @@ import { WorkspaceActivationStatus } from '~/generated/graphql';
export const useIsWorkspaceActivationStatusSuspended = (): boolean => { export const useIsWorkspaceActivationStatusSuspended = (): boolean => {
const currentWorkspace = useRecoilValue(currentWorkspaceState); const currentWorkspace = useRecoilValue(currentWorkspaceState);
return ( return (
currentWorkspace?.activationStatus === WorkspaceActivationStatus.Suspended currentWorkspace?.activationStatus === WorkspaceActivationStatus.SUSPENDED
); );
}; };

View File

@ -45,7 +45,7 @@ export const mockCurrentWorkspace: Workspace = {
logo: workspaceLogoUrl, logo: workspaceLogoUrl,
isPublicInviteLinkEnabled: true, isPublicInviteLinkEnabled: true,
allowImpersonation: true, allowImpersonation: true,
activationStatus: WorkspaceActivationStatus.Active, activationStatus: WorkspaceActivationStatus.ACTIVE,
hasValidEntrepriseKey: false, hasValidEntrepriseKey: false,
isGoogleAuthEnabled: true, isGoogleAuthEnabled: true,
isPasswordAuthEnabled: true, isPasswordAuthEnabled: true,

View File

@ -1,16 +1,13 @@
import chalk from 'chalk'; import chalk from 'chalk';
import { Option } from 'nest-commander'; import { Option } from 'nest-commander';
import { Repository } from 'typeorm'; import { WorkspaceActivationStatus } from 'twenty-shared';
import { In, Repository } from 'typeorm';
import { import {
BaseCommandOptions, BaseCommandOptions,
BaseCommandRunner, BaseCommandRunner,
} from 'src/database/commands/base.command'; } from 'src/database/commands/base.command';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
export type ActiveWorkspacesCommandOptions = BaseCommandOptions & { export type ActiveWorkspacesCommandOptions = BaseCommandOptions & {
workspaceId?: string; workspaceId?: string;
}; };
@ -38,7 +35,10 @@ export abstract class ActiveWorkspacesCommandRunner extends BaseCommandRunner {
const activeWorkspaces = await this.workspaceRepository.find({ const activeWorkspaces = await this.workspaceRepository.find({
select: ['id'], select: ['id'],
where: { where: {
activationStatus: WorkspaceActivationStatus.ACTIVE, activationStatus: In([
WorkspaceActivationStatus.ACTIVE,
WorkspaceActivationStatus.SUSPENDED,
]),
}, },
}); });

View File

@ -2,6 +2,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import chalk from 'chalk'; import chalk from 'chalk';
import { Command, Option } from 'nest-commander'; import { Command, Option } from 'nest-commander';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { In, Repository } from 'typeorm'; import { In, Repository } from 'typeorm';
import { import {
@ -16,10 +17,7 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-
import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity'; import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity';
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 { User } from 'src/engine/core-modules/user/user.entity'; import { User } from 'src/engine/core-modules/user/user.entity';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity';
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';

View File

@ -1,7 +1,6 @@
import { WorkspaceActivationStatus } from 'twenty-shared';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { WorkspaceActivationStatus } from 'src/engine/core-modules/workspace/workspace.entity';
const tableName = 'workspace'; const tableName = 'workspace';
export const seedWorkspaces = async ( export const seedWorkspaces = async (

View File

@ -1,9 +1,7 @@
import { WorkspaceActivationStatus } from 'twenty-shared';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
const tableName = 'workspace'; const tableName = 'workspace';

View File

@ -30,6 +30,7 @@ export const seedMessageChannel = async (
'type', 'type',
'connectedAccountId', 'connectedAccountId',
'handle', 'handle',
'isSyncEnabled',
'visibility', 'visibility',
'syncStage', 'syncStage',
]) ])
@ -46,7 +47,7 @@ export const seedMessageChannel = async (
handle: 'tim@apple.dev', handle: 'tim@apple.dev',
isSyncEnabled: false, isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING, visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncSubStatus: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING, syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
}, },
{ {
id: DEV_SEED_MESSAGE_CHANNEL_IDS.JONY, id: DEV_SEED_MESSAGE_CHANNEL_IDS.JONY,
@ -59,7 +60,7 @@ export const seedMessageChannel = async (
handle: 'jony.ive@apple.dev', handle: 'jony.ive@apple.dev',
isSyncEnabled: false, isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING, visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncSubStatus: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING, syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
}, },
{ {
id: DEV_SEED_MESSAGE_CHANNEL_IDS.PHIL, id: DEV_SEED_MESSAGE_CHANNEL_IDS.PHIL,
@ -72,7 +73,7 @@ export const seedMessageChannel = async (
handle: 'phil.schiler@apple.dev', handle: 'phil.schiler@apple.dev',
isSyncEnabled: false, isSyncEnabled: false,
visibility: MessageChannelVisibility.SHARE_EVERYTHING, visibility: MessageChannelVisibility.SHARE_EVERYTHING,
syncSubStatus: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING, syncStage: MessageChannelSyncStage.FULL_MESSAGE_LIST_FETCH_PENDING,
}, },
]) ])
.execute(); .execute();

View File

@ -1,28 +1,26 @@
import { Test, TestingModule } from '@nestjs/testing';
import { HttpService } from '@nestjs/axios'; import { HttpService } from '@nestjs/axios';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm'; import { getRepositoryToken } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service'; import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service'; import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
import { import {
AuthProviderWithPasswordType, AuthProviderWithPasswordType,
ExistingUserOrPartialUserWithPicture, ExistingUserOrPartialUserWithPicture,
SignInUpBaseParams, SignInUpBaseParams,
} from 'src/engine/core-modules/auth/types/signInUp.type'; } from 'src/engine/core-modules/auth/types/signInUp.type';
import { import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
Workspace, import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
WorkspaceActivationStatus, import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
} from 'src/engine/core-modules/workspace/workspace.entity'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { UserService } from 'src/engine/core-modules/user/services/user.service'; import { UserService } from 'src/engine/core-modules/user/services/user.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
jest.mock('src/utils/image', () => { jest.mock('src/utils/image', () => {
return { return {

View File

@ -3,12 +3,16 @@ import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import FileType from 'file-type'; import FileType from 'file-type';
import { TWENTY_ICONS_BASE_URL } from 'twenty-shared'; import {
TWENTY_ICONS_BASE_URL,
WorkspaceActivationStatus,
} from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface';
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
import { import {
AuthException, AuthException,
AuthExceptionCode, AuthExceptionCode,
@ -18,22 +22,6 @@ import {
compareHash, compareHash,
hashPassword, hashPassword,
} from 'src/engine/core-modules/auth/auth.util'; } from 'src/engine/core-modules/auth/auth.util';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
import {
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { getDomainNameByEmail } from 'src/utils/get-domain-name-by-email';
import { getImageBufferFromUrl } from 'src/utils/image';
import { isWorkEmail } from 'src/utils/is-work-email';
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
import { import {
AuthProviderWithPasswordType, AuthProviderWithPasswordType,
ExistingUserOrPartialUserWithPicture, ExistingUserOrPartialUserWithPicture,
@ -41,7 +29,19 @@ import {
SignInUpBaseParams, SignInUpBaseParams,
SignInUpNewUserPayload, SignInUpNewUserPayload,
} from 'src/engine/core-modules/auth/types/signInUp.type'; } from 'src/engine/core-modules/auth/types/signInUp.type';
import { DomainManagerService } from 'src/engine/core-modules/domain-manager/service/domain-manager.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service';
import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service';
import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service';
import { UserService } from 'src/engine/core-modules/user/services/user.service'; import { UserService } from 'src/engine/core-modules/user/services/user.service';
import { User } from 'src/engine/core-modules/user/user.entity';
import { WorkspaceInvitationService } from 'src/engine/core-modules/workspace-invitation/services/workspace-invitation.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { getDomainNameByEmail } from 'src/utils/get-domain-name-by-email';
import { getImageBufferFromUrl } from 'src/utils/image';
import { isWorkEmail } from 'src/utils/is-work-email';
@Injectable() @Injectable()
// eslint-disable-next-line @nx/workspace-inject-workspace-repository // eslint-disable-next-line @nx/workspace-inject-workspace-repository

View File

@ -2,6 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm'; import { getRepositoryToken } from '@nestjs/typeorm';
import { Request } from 'express'; import { Request } from 'express';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity'; import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity';
@ -12,10 +13,7 @@ import { EnvironmentService } from 'src/engine/core-modules/environment/environm
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service'; import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
import { SSOService } from 'src/engine/core-modules/sso/services/sso.service'; import { SSOService } from 'src/engine/core-modules/sso/services/sso.service';
import { User } from 'src/engine/core-modules/user/user.entity'; import { User } from 'src/engine/core-modules/user/user.entity';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { AccessTokenService } from './access-token.service'; import { AccessTokenService } from './access-token.service';

View File

@ -5,6 +5,7 @@ import { addMilliseconds } from 'date-fns';
import { Request } from 'express'; import { Request } from 'express';
import ms from 'ms'; import ms from 'ms';
import { ExtractJwt } from 'passport-jwt'; import { ExtractJwt } from 'passport-jwt';
import { isWorkspaceActiveOrSuspended } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { import {
@ -20,14 +21,11 @@ import {
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service'; import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service'; import { JwtWrapperService } from 'src/engine/core-modules/jwt/services/jwt-wrapper.service';
import { User } from 'src/engine/core-modules/user/user.entity'; import { User } from 'src/engine/core-modules/user/user.entity';
import { import { userValidator } from 'src/engine/core-modules/user/user.validate';
Workspace, import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
WorkspaceActivationStatus, import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
} from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate';
import { userValidator } from 'src/engine/core-modules/user/user.validate';
@Injectable() @Injectable()
export class AccessTokenService { export class AccessTokenService {
@ -67,7 +65,7 @@ export class AccessTokenService {
workspaceValidator.assertIsDefinedOrThrow(workspace); workspaceValidator.assertIsDefinedOrThrow(workspace);
if (workspace.activationStatus === WorkspaceActivationStatus.ACTIVE) { if (isWorkspaceActiveOrSuspended(workspace)) {
const workspaceMemberRepository = const workspaceMemberRepository =
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>( await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkspaceMemberWorkspaceEntity>(
workspaceId, workspaceId,

View File

@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import Stripe from 'stripe'; import Stripe from 'stripe';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity'; import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity';
@ -12,10 +13,7 @@ import { StripeCustomerService } from 'src/engine/core-modules/billing/stripe/se
import { transformStripeSubscriptionEventToCustomerRepositoryData } from 'src/engine/core-modules/billing/utils/transform-stripe-subscription-event-to-customer-repository-data.util'; import { transformStripeSubscriptionEventToCustomerRepositoryData } from 'src/engine/core-modules/billing/utils/transform-stripe-subscription-event-to-customer-repository-data.util';
import { transformStripeSubscriptionEventToSubscriptionItemRepositoryData } from 'src/engine/core-modules/billing/utils/transform-stripe-subscription-event-to-subscription-item-repository-data.util'; import { transformStripeSubscriptionEventToSubscriptionItemRepositoryData } from 'src/engine/core-modules/billing/utils/transform-stripe-subscription-event-to-subscription-item-repository-data.util';
import { transformStripeSubscriptionEventToSubscriptionRepositoryData } from 'src/engine/core-modules/billing/utils/transform-stripe-subscription-event-to-subscription-repository-data.util'; import { transformStripeSubscriptionEventToSubscriptionRepositoryData } from 'src/engine/core-modules/billing/utils/transform-stripe-subscription-event-to-subscription-repository-data.util';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
@Injectable() @Injectable()
export class BillingWebhookSubscriptionService { export class BillingWebhookSubscriptionService {
protected readonly logger = new Logger( protected readonly logger = new Logger(
@ -88,10 +86,11 @@ export class BillingWebhookSubscriptionService {
if ( if (
data.object.status === SubscriptionStatus.Canceled || data.object.status === SubscriptionStatus.Canceled ||
data.object.status === SubscriptionStatus.Unpaid data.object.status === SubscriptionStatus.Unpaid ||
data.object.status === SubscriptionStatus.Paused
) { ) {
await this.workspaceRepository.update(workspaceId, { await this.workspaceRepository.update(workspaceId, {
activationStatus: WorkspaceActivationStatus.INACTIVE, activationStatus: WorkspaceActivationStatus.SUSPENDED,
}); });
} }
@ -99,7 +98,7 @@ export class BillingWebhookSubscriptionService {
(data.object.status === SubscriptionStatus.Active || (data.object.status === SubscriptionStatus.Active ||
data.object.status === SubscriptionStatus.Trialing || data.object.status === SubscriptionStatus.Trialing ||
data.object.status === SubscriptionStatus.PastDue) && data.object.status === SubscriptionStatus.PastDue) &&
workspace.activationStatus == WorkspaceActivationStatus.INACTIVE workspace.activationStatus == WorkspaceActivationStatus.SUSPENDED
) { ) {
await this.workspaceRepository.update(workspaceId, { await this.workspaceRepository.update(workspaceId, {
activationStatus: WorkspaceActivationStatus.ACTIVE, activationStatus: WorkspaceActivationStatus.ACTIVE,

View File

@ -1,13 +1,12 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { BillingService } from 'src/engine/core-modules/billing/services/billing.service'; import { BillingService } from 'src/engine/core-modules/billing/services/billing.service';
import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum'; import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum';
import { UserVarsService } from 'src/engine/core-modules/user/user-vars/services/user-vars.service'; import { UserVarsService } from 'src/engine/core-modules/user/user-vars/services/user-vars.service';
import { User } from 'src/engine/core-modules/user/user.entity'; import { User } from 'src/engine/core-modules/user/user.entity';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
export enum OnboardingStepKeys { export enum OnboardingStepKeys {
ONBOARDING_CONNECT_ACCOUNT_PENDING = 'ONBOARDING_CONNECT_ACCOUNT_PENDING', ONBOARDING_CONNECT_ACCOUNT_PENDING = 'ONBOARDING_CONNECT_ACCOUNT_PENDING',

View File

@ -3,6 +3,7 @@ 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 { isWorkspaceActiveOrSuspended } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { TypeORMService } from 'src/database/typeorm/typeorm.service'; import { TypeORMService } from 'src/database/typeorm/typeorm.service';
@ -14,10 +15,7 @@ import {
import { User } from 'src/engine/core-modules/user/user.entity'; import { User } from 'src/engine/core-modules/user/user.entity';
import { userValidator } from 'src/engine/core-modules/user/user.validate'; import { userValidator } from 'src/engine/core-modules/user/user.validate';
import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service'; import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} 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 { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@ -41,10 +39,7 @@ export class UserService extends TypeOrmQueryService<User> {
} }
async loadWorkspaceMember(user: User, workspace: Workspace) { async loadWorkspaceMember(user: User, workspace: Workspace) {
if ( if (!isWorkspaceActiveOrSuspended(workspace)) {
workspace?.activationStatus !== WorkspaceActivationStatus.ACTIVE &&
workspace?.activationStatus !== WorkspaceActivationStatus.SUSPENDED
) {
return null; return null;
} }
@ -62,7 +57,7 @@ export class UserService extends TypeOrmQueryService<User> {
} }
async loadWorkspaceMembers(workspace: Workspace) { async loadWorkspaceMembers(workspace: Workspace) {
if (workspace.activationStatus !== WorkspaceActivationStatus.ACTIVE) { if (!isWorkspaceActiveOrSuspended(workspace)) {
return []; return [];
} }

View File

@ -4,6 +4,7 @@ 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 { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service'; import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
@ -14,10 +15,7 @@ import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-works
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 { User } from 'src/engine/core-modules/user/user.entity'; import { User } from 'src/engine/core-modules/user/user.entity';
import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input'; import { ActivateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/activate-workspace-input';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { import {
WorkspaceException, WorkspaceException,
WorkspaceExceptionCode, WorkspaceExceptionCode,

View File

@ -1,6 +1,7 @@
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import { IDField, UnPagedRelation } from '@ptc-org/nestjs-query-graphql'; import { IDField, UnPagedRelation } from '@ptc-org/nestjs-query-graphql';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { import {
Column, Column,
CreateDateColumn, CreateDateColumn,
@ -23,14 +24,6 @@ import { PostgresCredentials } from 'src/engine/core-modules/postgres-credential
import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity'; import { WorkspaceSSOIdentityProvider } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
export enum WorkspaceActivationStatus {
ONGOING_CREATION = 'ONGOING_CREATION',
PENDING_CREATION = 'PENDING_CREATION',
ACTIVE = 'ACTIVE',
INACTIVE = 'INACTIVE',
SUSPENDED = 'SUSPENDED',
}
registerEnumType(WorkspaceActivationStatus, { registerEnumType(WorkspaceActivationStatus, {
name: 'WorkspaceActivationStatus', name: 'WorkspaceActivationStatus',
}); });

View File

@ -1,17 +1,16 @@
import { import { WorkspaceActivationStatus } from 'twenty-shared';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { CustomException } from 'src/utils/custom-exception';
import { import {
AuthException, AuthException,
AuthExceptionCode, AuthExceptionCode,
} from 'src/engine/core-modules/auth/auth.exception'; } from 'src/engine/core-modules/auth/auth.exception';
import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type'; import { WorkspaceAuthProvider } from 'src/engine/core-modules/workspace/types/workspace.type';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { import {
WorkspaceException, WorkspaceException,
WorkspaceExceptionCode, WorkspaceExceptionCode,
} from 'src/engine/core-modules/workspace/workspace.exception'; } from 'src/engine/core-modules/workspace/workspace.exception';
import { CustomException } from 'src/utils/custom-exception';
const assertIsDefinedOrThrow = ( const assertIsDefinedOrThrow = (
workspace: Workspace | undefined | null, workspace: Workspace | undefined | null,

View File

@ -1,5 +1,6 @@
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Any, Repository } from 'typeorm'; import { Any, Repository } from 'typeorm';
import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator'; import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator';
@ -9,10 +10,7 @@ import { Process } from 'src/engine/core-modules/message-queue/decorators/proces
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { import {
CalendarEventListFetchJob, CalendarEventListFetchJob,

View File

@ -1,5 +1,6 @@
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Equal, Repository } from 'typeorm'; import { Equal, Repository } from 'typeorm';
import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator'; import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator';
@ -9,15 +10,11 @@ import { Process } from 'src/engine/core-modules/message-queue/decorators/proces
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { CalendarEventListFetchJobData } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job'; import { CalendarEventListFetchJobData } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-event-list-fetch.job';
import { CalendarEventsImportJob } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job'; import { CalendarEventsImportJob } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-events-import.job';
import { CalendarChannelSyncStage } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity'; import { CalendarChannelSyncStage } from 'src/modules/calendar/common/standard-objects/calendar-channel.workspace-entity';
export const CALENDAR_EVENTS_IMPORT_CRON_PATTERN = '*/1 * * * *'; export const CALENDAR_EVENTS_IMPORT_CRON_PATTERN = '*/1 * * * *';
@Processor({ @Processor({

View File

@ -1,5 +1,6 @@
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator'; import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator';
@ -9,15 +10,11 @@ import { Process } from 'src/engine/core-modules/message-queue/decorators/proces
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { import {
CalendarOngoingStaleJob, CalendarOngoingStaleJob,
CalendarOngoingStaleJobData, CalendarOngoingStaleJobData,
} from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job'; } from 'src/modules/calendar/calendar-event-import-manager/jobs/calendar-ongoing-stale.job';
export const CALENDAR_ONGOING_STALE_CRON_PATTERN = '0 * * * *'; export const CALENDAR_ONGOING_STALE_CRON_PATTERN = '0 * * * *';
@Processor(MessageQueue.cronQueue) @Processor(MessageQueue.cronQueue)

View File

@ -1,5 +1,6 @@
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { In, Repository } from 'typeorm'; import { In, Repository } from 'typeorm';
import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator'; import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator';
@ -9,10 +10,7 @@ import { Process } from 'src/engine/core-modules/message-queue/decorators/proces
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { import {
MessageChannelSyncStage, MessageChannelSyncStage,
@ -22,7 +20,6 @@ import {
MessagingMessageListFetchJob, MessagingMessageListFetchJob,
MessagingMessageListFetchJobData, MessagingMessageListFetchJobData,
} from 'src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job'; } from 'src/modules/messaging/message-import-manager/jobs/messaging-message-list-fetch.job';
export const MESSAGING_MESSAGE_LIST_FETCH_CRON_PATTERN = '*/5 * * * *'; export const MESSAGING_MESSAGE_LIST_FETCH_CRON_PATTERN = '*/5 * * * *';
@Processor(MessageQueue.cronQueue) @Processor(MessageQueue.cronQueue)

View File

@ -1,5 +1,6 @@
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator'; import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator';
@ -9,10 +10,7 @@ import { Process } from 'src/engine/core-modules/message-queue/decorators/proces
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { import {
MessageChannelSyncStage, MessageChannelSyncStage,

View File

@ -1,5 +1,6 @@
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator'; import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator';
@ -9,15 +10,11 @@ import { Process } from 'src/engine/core-modules/message-queue/decorators/proces
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { import {
MessagingOngoingStaleJob, MessagingOngoingStaleJob,
MessagingOngoingStaleJobData, MessagingOngoingStaleJobData,
} from 'src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job'; } from 'src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job';
export const MESSAGING_ONGOING_STALE_CRON_PATTERN = '0 * * * *'; export const MESSAGING_ONGOING_STALE_CRON_PATTERN = '0 * * * *';
@Processor(MessageQueue.cronQueue) @Processor(MessageQueue.cronQueue)

View File

@ -1,18 +1,18 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
Workspace, import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event'; import { ObjectRecordCreateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-create.event';
import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event'; import { ObjectRecordUpdateEvent } from 'src/engine/core-modules/event-emitter/types/object-record-update.event';
import { objectRecordChangedProperties as objectRecordUpdateEventChangedProperties } from 'src/engine/core-modules/event-emitter/utils/object-record-changed-properties.util'; import { objectRecordChangedProperties as objectRecordUpdateEventChangedProperties } from 'src/engine/core-modules/event-emitter/utils/object-record-changed-properties.util';
import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator'; import { InjectMessageQueue } from 'src/engine/core-modules/message-queue/decorators/message-queue.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service'; import { MessageQueueService } from 'src/engine/core-modules/message-queue/services/message-queue.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type'; import { WorkspaceEventBatch } from 'src/engine/workspace-event-emitter/types/workspace-event.type';
import { import {
MessageParticipantMatchParticipantJob, MessageParticipantMatchParticipantJob,
@ -23,9 +23,6 @@ import {
MessageParticipantUnmatchParticipantJobData, MessageParticipantUnmatchParticipantJobData,
} from 'src/modules/messaging/message-participant-manager/jobs/message-participant-unmatch-participant.job'; } from 'src/modules/messaging/message-participant-manager/jobs/message-participant-unmatch-participant.job';
import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity';
import { OnDatabaseBatchEvent } from 'src/engine/api/graphql/graphql-query-runner/decorators/on-database-batch-event.decorator';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
@Injectable() @Injectable()
export class MessageParticipantWorkspaceMemberListener { export class MessageParticipantWorkspaceMemberListener {
constructor( constructor(

View File

@ -2,6 +2,7 @@ import { Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import snakeCase from 'lodash.snakecase'; import snakeCase from 'lodash.snakecase';
import { WorkspaceActivationStatus } from 'twenty-shared';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator'; import { SentryCronMonitor } from 'src/engine/core-modules/cron/sentry-cron-monitor.decorator';
@ -9,10 +10,7 @@ import { ExceptionHandlerService } from 'src/engine/core-modules/exception-handl
import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator'; import { Process } from 'src/engine/core-modules/message-queue/decorators/process.decorator';
import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator'; import { Processor } from 'src/engine/core-modules/message-queue/decorators/processor.decorator';
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
Workspace,
WorkspaceActivationStatus,
} from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity';
import { MessagingTelemetryService } from 'src/modules/messaging/monitoring/services/messaging-telemetry.service'; import { MessagingTelemetryService } from 'src/modules/messaging/monitoring/services/messaging-telemetry.service';

View File

@ -4,4 +4,4 @@ export * from './types/FieldMetadataType';
export * from './utils/fieldMetadata/isFieldMetadataDateKind'; export * from './utils/fieldMetadata/isFieldMetadataDateKind';
export * from './utils/image/getImageAbsoluteURI'; export * from './utils/image/getImageAbsoluteURI';
export * from './utils/strings'; export * from './utils/strings';
export * from './workspace';

View File

@ -0,0 +1,2 @@
export * from './types/WorkspaceActivationStatus';
export * from './utils/isWorkspaceActiveOrSuspended';

View File

@ -0,0 +1,7 @@
export enum WorkspaceActivationStatus {
ONGOING_CREATION = 'ONGOING_CREATION',
PENDING_CREATION = 'PENDING_CREATION',
ACTIVE = 'ACTIVE',
INACTIVE = 'INACTIVE',
SUSPENDED = 'SUSPENDED',
}

View File

@ -0,0 +1,52 @@
import { WorkspaceActivationStatus } from '../../types/WorkspaceActivationStatus';
import { isWorkspaceActiveOrSuspended } from '../isWorkspaceActiveOrSuspended';
describe('isWorkspaceActiveOrSuspended', () => {
it('should return true for Active workspace', () => {
const workspace = {
activationStatus: WorkspaceActivationStatus.ACTIVE,
};
expect(isWorkspaceActiveOrSuspended(workspace)).toBe(true);
});
it('should return true for Suspended workspace', () => {
const workspace = {
activationStatus: WorkspaceActivationStatus.SUSPENDED,
};
expect(isWorkspaceActiveOrSuspended(workspace)).toBe(true);
});
it('should return false for Inactive workspace', () => {
const workspace = {
activationStatus: WorkspaceActivationStatus.INACTIVE,
};
expect(isWorkspaceActiveOrSuspended(workspace)).toBe(false);
});
it('should return false for OngoingCreation workspace', () => {
const workspace = {
activationStatus: WorkspaceActivationStatus.ONGOING_CREATION,
};
expect(isWorkspaceActiveOrSuspended(workspace)).toBe(false);
});
it('should return false for PendingCreation workspace', () => {
const workspace = {
activationStatus: WorkspaceActivationStatus.PENDING_CREATION,
};
expect(isWorkspaceActiveOrSuspended(workspace)).toBe(false);
});
it('should return false for undefined workspace', () => {
expect(isWorkspaceActiveOrSuspended(undefined)).toBe(false);
});
it('should return false for null workspace', () => {
expect(isWorkspaceActiveOrSuspended(null)).toBe(false);
});
});

View File

@ -0,0 +1,12 @@
import { WorkspaceActivationStatus } from '../types/WorkspaceActivationStatus';
export const isWorkspaceActiveOrSuspended = (
workspace?: {
activationStatus: WorkspaceActivationStatus;
} | null,
): boolean => {
return (
workspace?.activationStatus === WorkspaceActivationStatus.ACTIVE ||
workspace?.activationStatus === WorkspaceActivationStatus.SUSPENDED
);
};