add new @WorkspaceIsSearchable decorator + updates services + add migration command (#10507)

closes https://github.com/twentyhq/core-team-issues/issues/345
This commit is contained in:
Etienne
2025-02-27 13:57:07 +01:00
committed by GitHub
parent 17dbb634ca
commit 39543872e6
54 changed files with 297 additions and 145 deletions

View File

@ -19,6 +19,7 @@ export const mockPersonObjectMetadata = (
isActive: true,
isSystem: false,
isAuditLogged: true,
isSearchable: true,
duplicateCriteria: duplicateCriteria,
fromRelations: [],
toRelations: [],

View File

@ -6,6 +6,7 @@ import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/work
import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface';
import { FindDuplicatesResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory';
import { SearchResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/search-resolver-factory';
@Injectable()
export class WorkspaceResolverBuilderService {
@ -18,6 +19,8 @@ export class WorkspaceResolverBuilderService {
switch (methodName) {
case FindDuplicatesResolverFactory.methodName:
return isDefined(objectMetadata.duplicateCriteria);
case SearchResolverFactory.methodName:
return objectMetadata.isSearchable;
default:
return true;
}

View File

@ -7,22 +7,22 @@ import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import {
ActiveWorkspacesMigrationCommandOptions,
ActiveWorkspacesMigrationCommandRunner,
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
MaintainedWorkspacesMigrationCommandOptions,
MaintainedWorkspacesMigrationCommandRunner,
} from 'src/database/commands/migration-command/maintained-workspaces-migration-command.runner';
import { BillingCustomer } from 'src/engine/core-modules/billing/entities/billing-customer.entity';
import { StripeSubscriptionService } from 'src/engine/core-modules/billing/stripe/services/stripe-subscription.service';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
interface SyncCustomerDataCommandOptions
extends ActiveWorkspacesMigrationCommandOptions {}
extends MaintainedWorkspacesMigrationCommandOptions {}
@Command({
name: 'billing:sync-customer-data',
description: 'Sync customer data from Stripe for all active workspaces',
})
export class BillingSyncCustomerDataCommand extends ActiveWorkspacesMigrationCommandRunner {
export class BillingSyncCustomerDataCommand extends MaintainedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@ -34,7 +34,7 @@ export class BillingSyncCustomerDataCommand extends ActiveWorkspacesMigrationCom
super(workspaceRepository, twentyORMGlobalManager);
}
async runMigrationCommandOnActiveWorkspaces(
async runMigrationCommandOnMaintainedWorkspaces(
_passedParam: string[],
options: SyncCustomerDataCommandOptions,
workspaceIds: string[],

View File

@ -20,6 +20,7 @@ const mockObjectMetadata: ObjectMetadataInterface = {
isActive: true,
isRemote: false,
isAuditLogged: true,
isSearchable: true,
};
describe('objectRecordChangedValues', () => {

View File

@ -18,6 +18,7 @@ export const mockObjectMetadataItemsWithFieldMaps: ObjectMetadataItemWithFieldMa
isActive: true,
isSystem: false,
isAuditLogged: true,
isSearchable: true,
fromRelations: [],
toRelations: [],
labelIdentifierFieldMetadataId: 'nameFieldMetadataId',
@ -76,6 +77,7 @@ export const mockObjectMetadataItemsWithFieldMaps: ObjectMetadataItemWithFieldMa
isActive: true,
isSystem: false,
isAuditLogged: true,
isSearchable: true,
fromRelations: [],
toRelations: [],
labelIdentifierFieldMetadataId: 'nameFieldMetadataId',
@ -153,6 +155,7 @@ export const mockObjectMetadataItemsWithFieldMaps: ObjectMetadataItemWithFieldMa
isActive: true,
isSystem: false,
isAuditLogged: true,
isSearchable: true,
fromRelations: [],
toRelations: [],
labelIdentifierFieldMetadataId: 'nameFieldMetadataId',
@ -230,6 +233,7 @@ export const mockObjectMetadataItemsWithFieldMaps: ObjectMetadataItemWithFieldMa
isActive: true,
isSystem: true,
isAuditLogged: true,
isSearchable: false,
fromRelations: [],
toRelations: [],
labelIdentifierFieldMetadataId: '',

View File

@ -19,7 +19,7 @@ describe('GlobalSearchService', () => {
});
describe('filterObjectMetadataItems', () => {
it('should return searchable object metadata items -- TODO isSearchable only', () => {
it('should return searchable object metadata items', () => {
const objectMetadataItems = service.filterObjectMetadataItems(
mockObjectMetadataItemsWithFieldMaps,
[],

View File

@ -22,20 +22,9 @@ export class GlobalSearchService {
excludedObjectNameSingulars: string[] | undefined,
) {
return objectMetadataItemWithFieldMaps.filter(
({ nameSingular, isSystem, isRemote, isCustom }) => {
if (excludedObjectNameSingulars?.includes(nameSingular)) {
return false;
}
//TODO - #345 issue - IsSearchable decorator
if (isSystem || isRemote) {
return false;
}
({ nameSingular, isSearchable }) => {
return (
isCustom ||
['company', 'person', 'opportunity', 'note', 'task'].includes(
nameSingular,
)
!excludedObjectNameSingulars?.includes(nameSingular) && isSearchable
);
},
);

View File

@ -24,6 +24,7 @@ export interface ObjectMetadataInterface {
isActive: boolean;
isRemote: boolean;
isAuditLogged: boolean;
isSearchable: boolean;
duplicateCriteria?: WorkspaceEntityDuplicateCriteria[];
labelIdentifierFieldMetadataId?: string | null;
imageIdentifierFieldMetadataId?: string | null;

View File

@ -69,6 +69,9 @@ export class ObjectMetadataDTO {
@FilterableField()
isSystem: boolean;
@FilterableField()
isSearchable: boolean;
@HideField()
workspaceId: string;

View File

@ -70,6 +70,9 @@ export class ObjectMetadataEntity implements ObjectMetadataInterface {
@Column({ default: true })
isAuditLogged: boolean;
@Column({ default: false })
isSearchable: boolean;
@Column({ type: 'jsonb', nullable: true })
duplicateCriteria?: WorkspaceEntityDuplicateCriteria[];

View File

@ -120,6 +120,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
isCustom: !objectMetadataInput.isRemote,
isSystem: false,
isRemote: objectMetadataInput.isRemote,
isSearchable: !objectMetadataInput.isRemote,
fields: objectMetadataInput.isRemote
? []
: buildDefaultFieldsForCustomObject(objectMetadataInput.workspaceId),

View File

@ -37,6 +37,11 @@ export function WorkspaceEntity(
'workspace:duplicate-criteria-metadata-args',
target,
);
const isSearchable =
TypedReflect.getMetadata(
'workspace:is-searchable-metadata-args',
target,
) ?? false;
const objectName = convertClassNameToObjectMetadataName(target.name);
@ -57,6 +62,7 @@ export function WorkspaceEntity(
isSystem,
gate,
duplicateCriteria,
isSearchable,
});
};
}

View File

@ -0,0 +1,11 @@
import { TypedReflect } from 'src/utils/typed-reflect';
export function WorkspaceIsSearchable() {
return function (target: any): void {
TypedReflect.defineMetadata(
'workspace:is-searchable-metadata-args',
true,
target,
);
};
}

View File

@ -72,4 +72,9 @@ export interface WorkspaceEntityMetadataArgs {
* Duplicate criteria.
*/
readonly duplicateCriteria?: WorkspaceEntityDuplicateCriteria[];
/**
* Is searchable object.
*/
readonly isSearchable: boolean;
}

View File

@ -4,9 +4,9 @@ import { Command, Option } from 'nest-commander';
import { Repository } from 'typeorm';
import {
ActiveWorkspacesMigrationCommandOptions,
ActiveWorkspacesMigrationCommandRunner,
} from 'src/database/commands/migration-command/active-workspaces-migration-command.runner';
MaintainedWorkspacesMigrationCommandOptions,
MaintainedWorkspacesMigrationCommandRunner,
} from 'src/database/commands/migration-command/maintained-workspaces-migration-command.runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@ -16,7 +16,7 @@ import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/works
import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service';
interface RunWorkspaceMigrationsOptions
extends ActiveWorkspacesMigrationCommandOptions {
extends MaintainedWorkspacesMigrationCommandOptions {
force?: boolean;
}
@ -24,7 +24,7 @@ interface RunWorkspaceMigrationsOptions
name: 'workspace:sync-metadata',
description: 'Sync metadata',
})
export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesMigrationCommandRunner {
export class SyncWorkspaceMetadataCommand extends MaintainedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
@ -37,7 +37,7 @@ export class SyncWorkspaceMetadataCommand extends ActiveWorkspacesMigrationComma
super(workspaceRepository, twentyORMGlobalManager);
}
async runMigrationCommandOnActiveWorkspaces(
async runMigrationCommandOnMaintainedWorkspaces(
_passedParam: string[],
options: RunWorkspaceMigrationsOptions,
workspaceIds: string[],