[REQUIRES_CACHE_FLUSH][GQL_VIEW_GROUP_API_BREAKING_CHANGE] ViewGroup in v2 (#15052)

# Introduction
Adding view-group to core engine v2
Following https://github.com/twentyhq/twenty/pull/15010 ( same pattern )

## What's done
- Created flat-view-group
- flat view group runner
- flat view group builder
- create view group service v2 and input transpilers
- refactor the existing view group resolver to fix standard (
BREAKING_CHANGE on graphql api update especially ) REST stays the same
- refactored the front to consume the mutations autogenerated

close https://github.com/twentyhq/core-team-issues/issues/1665
This commit is contained in:
Paul Rastoin
2025-10-14 15:55:50 +02:00
committed by GitHub
parent 5fec6a9afc
commit 7d747c9876
88 changed files with 2640 additions and 497 deletions
@@ -0,0 +1,36 @@
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
import { v4 } from 'uuid';
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
import { type CreateViewGroupInput } from 'src/engine/metadata-modules/view-group/dtos/inputs/create-view-group.input';
export const fromCreateViewGroupInputToFlatViewGroupToCreate = ({
createViewGroupInput: rawCreateViewGroupInput,
workspaceId,
}: {
createViewGroupInput: CreateViewGroupInput;
workspaceId: string;
}): FlatViewGroup => {
const { fieldMetadataId, viewId, ...createViewGroupInput } =
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
rawCreateViewGroupInput,
['fieldMetadataId', 'fieldValue', 'id', 'viewId'],
);
const createdAt = new Date();
const viewGroupId = createViewGroupInput.id ?? v4();
return {
id: viewGroupId,
fieldMetadataId,
viewId,
workspaceId,
createdAt: createdAt,
updatedAt: createdAt,
deletedAt: null,
universalIdentifier: viewGroupId,
isVisible: createViewGroupInput.isVisible ?? true,
fieldValue: createViewGroupInput.fieldValue,
position: createViewGroupInput.position ?? 0,
};
};
@@ -0,0 +1,40 @@
import { t } from '@lingui/core/macro';
import {
extractAndSanitizeObjectStringFields,
isDefined,
} from 'twenty-shared/utils';
import { type FlatViewGroupMaps } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group-maps.type';
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
import { type DeleteViewGroupInput } from 'src/engine/metadata-modules/view-group/dtos/inputs/delete-view-group.input';
import {
ViewGroupException,
ViewGroupExceptionCode,
} from 'src/engine/metadata-modules/view-group/exceptions/view-group.exception';
export const fromDeleteViewGroupInputToFlatViewGroupOrThrow = ({
deleteViewGroupInput: rawDeleteViewGroupInput,
flatViewGroupMaps,
}: {
deleteViewGroupInput: DeleteViewGroupInput;
flatViewGroupMaps: FlatViewGroupMaps;
}): FlatViewGroup => {
const { id: viewGroupId } = extractAndSanitizeObjectStringFields(
rawDeleteViewGroupInput,
['id'],
);
const existingFlatViewGroupToDelete = flatViewGroupMaps.byId[viewGroupId];
if (!isDefined(existingFlatViewGroupToDelete)) {
throw new ViewGroupException(
t`View group to delete not found`,
ViewGroupExceptionCode.VIEW_GROUP_NOT_FOUND,
);
}
return {
...existingFlatViewGroupToDelete,
deletedAt: new Date(),
};
};
@@ -0,0 +1,37 @@
import { t } from '@lingui/core/macro';
import {
extractAndSanitizeObjectStringFields,
isDefined,
} from 'twenty-shared/utils';
import { type FlatViewGroupMaps } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group-maps.type';
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
import { type DestroyViewGroupInput } from 'src/engine/metadata-modules/view-group/dtos/inputs/destroy-view-group.input';
import {
ViewGroupException,
ViewGroupExceptionCode,
} from 'src/engine/metadata-modules/view-group/exceptions/view-group.exception';
export const fromDestroyViewGroupInputToFlatViewGroupOrThrow = ({
destroyViewGroupInput,
flatViewGroupMaps,
}: {
destroyViewGroupInput: DestroyViewGroupInput;
flatViewGroupMaps: FlatViewGroupMaps;
}): FlatViewGroup => {
const { id: viewGroupId } = extractAndSanitizeObjectStringFields(
destroyViewGroupInput,
['id'],
);
const existingFlatViewGroupToDestroy = flatViewGroupMaps.byId[viewGroupId];
if (!isDefined(existingFlatViewGroupToDestroy)) {
throw new ViewGroupException(
t`View group to destroy not found`,
ViewGroupExceptionCode.VIEW_GROUP_NOT_FOUND,
);
}
return existingFlatViewGroupToDestroy;
};
@@ -0,0 +1,50 @@
import { t } from '@lingui/core/macro';
import {
extractAndSanitizeObjectStringFields,
isDefined,
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties,
} from 'twenty-shared/utils';
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
import { FLAT_VIEW_GROUP_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-view-group/constants/flat-view-group-editable-properties.constant';
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
import { type UpdateViewGroupInput } from 'src/engine/metadata-modules/view-group/dtos/inputs/update-view-group.input';
import {
ViewGroupException,
ViewGroupExceptionCode,
} from 'src/engine/metadata-modules/view-group/exceptions/view-group.exception';
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
export const fromUpdateViewGroupInputToFlatViewGroupToUpdateOrThrow = ({
updateViewGroupInput: rawUpdateViewGroupInput,
flatViewGroupMaps,
}: {
updateViewGroupInput: UpdateViewGroupInput;
flatViewGroupMaps: FlatEntityMaps<FlatViewGroup>;
}): FlatViewGroup => {
const { id: viewGroupToUpdateId, update } =
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
rawUpdateViewGroupInput,
['id'],
);
const existingFlatViewGroupToUpdate =
flatViewGroupMaps.byId[viewGroupToUpdateId];
if (!isDefined(existingFlatViewGroupToUpdate)) {
throw new ViewGroupException(
t`View group to update not found`,
ViewGroupExceptionCode.VIEW_GROUP_NOT_FOUND,
);
}
const updatedEditableGroupProperties = extractAndSanitizeObjectStringFields(
update,
FLAT_VIEW_GROUP_EDITABLE_PROPERTIES,
);
return mergeUpdateInExistingRecord({
existing: existingFlatViewGroupToUpdate,
properties: FLAT_VIEW_GROUP_EDITABLE_PROPERTIES,
update: updatedEditableGroupProperties,
});
};
@@ -0,0 +1,21 @@
import { removePropertiesFromRecord } from 'twenty-shared/utils';
import { VIEW_GROUP_ENTITY_RELATION_PROPERTIES } from 'src/engine/metadata-modules/flat-view-group/constants/view-group-entity-relation-properties.constant';
import { type FlatViewGroup } from 'src/engine/metadata-modules/flat-view-group/types/flat-view-group.type';
import { type ViewGroupEntity } from 'src/engine/metadata-modules/view-group/entities/view-group.entity';
export const fromViewGroupEntityToFlatViewGroup = (
viewGroupEntity: ViewGroupEntity,
): FlatViewGroup => {
const viewGroupEntityWithoutRelations = removePropertiesFromRecord(
viewGroupEntity,
VIEW_GROUP_ENTITY_RELATION_PROPERTIES,
);
return {
...viewGroupEntityWithoutRelations,
universalIdentifier:
viewGroupEntityWithoutRelations.universalIdentifier ??
viewGroupEntityWithoutRelations.id,
};
};