Migrate all remaining workspace migration create action to universal (#17836)

# Introduction
Completely finalize the universal migration at builder and runner
levels.
Which mean that from now on the builder only compares
`universalFlatEntity` and produces universal workspace migration that
the runner ingest

## What's done
Migrated all create metadata remaining for
- agent
- skill
- commandMenuItem
- navigationMenuItem
- fieldMetadata
- objectMetadata
- view
- viewField
- viewFilter
- viewGroup
- viewFilterGroup
- index
- logicFunction
- role
- roleTarget
- pageLayout
- pageLayoutTab
- pageLayoutWidget
- rowLevelPermissionPredicate
- rowLevelPermissionPredicateGroup
- frontComponent
This commit is contained in:
Paul Rastoin
2026-02-10 14:39:37 +01:00
committed by GitHub
parent b7ff587b5e
commit 148584c730
74 changed files with 1012 additions and 391 deletions
@@ -1,6 +0,0 @@
import { type GraphConfiguration } from './graph-configuration.type';
export type BaseGraphConfiguration = Pick<
GraphConfiguration,
'configurationType' | 'aggregateFieldMetadataId' | 'aggregateOperation'
>;
@@ -2,16 +2,24 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type BarChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateBarChartConfiguration = (
configuration: BarChartConfigurationDTO,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
export const validateBarChartConfiguration = ({
graphUniversalConfiguration,
widgetTitle,
}: {
graphUniversalConfiguration: UniversalFlatPageLayoutWidget<WidgetConfigurationType.BAR_CHART>['universalConfiguration'];
widgetTitle: string;
}): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.primaryAxisGroupByFieldMetadataId)) {
if (
!isDefined(
graphUniversalConfiguration.primaryAxisGroupByFieldMetadataUniversalIdentifier,
)
) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Primary axis group by field is required for bar chart widget "${widgetTitle}"`,
@@ -19,7 +27,7 @@ export const validateBarChartConfiguration = (
});
}
if (!isDefined(configuration.layout)) {
if (!isDefined(graphUniversalConfiguration.layout)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Layout is required for bar chart widget "${widgetTitle}"`,
@@ -2,24 +2,32 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type BaseGraphConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/base-graph-configuration.type';
import { type AllGraphWidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateBaseGraphFields = (
configuration: BaseGraphConfiguration,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
export const validateBaseGraphFields = ({
graphUniversalConfiguration,
widgetTitle,
}: {
graphUniversalConfiguration: UniversalFlatPageLayoutWidget<AllGraphWidgetConfigurationType>['universalConfiguration'];
widgetTitle: string;
}): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.aggregateFieldMetadataId)) {
if (
!isDefined(
graphUniversalConfiguration.aggregateFieldMetadataUniversalIdentifier,
)
) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Aggregate field metadata ID is required for graph widget "${widgetTitle}"`,
message: t`Aggregate field metadata is required for graph widget "${widgetTitle}"`,
userFriendlyMessage: msg`Aggregate field is required for graph widget`,
});
}
if (!isDefined(configuration.aggregateOperation)) {
if (!isDefined(graphUniversalConfiguration.aggregateOperation)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Aggregate operation is required for graph widget "${widgetTitle}"`,
@@ -2,20 +2,23 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type FrontComponentConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/front-component-configuration.dto';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateFrontComponentConfigurationType = (
configuration: FrontComponentConfigurationDTO,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
export const validateFrontComponentConfigurationType = ({
universalConfiguration,
title,
}: Pick<
UniversalFlatPageLayoutWidget,
'universalConfiguration' | 'title'
>): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.configurationType)) {
if (!isDefined(universalConfiguration.configurationType)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration type is required for widget "${widgetTitle}"`,
message: t`Configuration type is required for widget "${title}"`,
userFriendlyMessage: msg`Configuration type is required`,
});
@@ -23,15 +26,18 @@ export const validateFrontComponentConfigurationType = (
}
if (
configuration.configurationType !== WidgetConfigurationType.FRONT_COMPONENT
universalConfiguration.configurationType !==
WidgetConfigurationType.FRONT_COMPONENT
) {
const configurationTypeString = String(configuration.configurationType);
const configurationTypeString = String(
universalConfiguration.configurationType,
);
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Invalid configuration type for front component widget "${widgetTitle}". Expected FRONT_COMPONENT, got ${configurationTypeString}`,
message: t`Invalid configuration type for front component widget "${title}". Expected FRONT_COMPONENT, got ${configurationTypeString}`,
userFriendlyMessage: msg`Invalid configuration type for front component widget`,
value: configuration.configurationType,
value: universalConfiguration.configurationType,
});
}
@@ -4,17 +4,16 @@ import { isDefined } from 'twenty-shared/utils';
import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForCreationArgs } from 'src/engine/metadata-modules/flat-page-layout-widget/services/flat-page-layout-widget-type-validator.service';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { validateFrontComponentConfigurationType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-front-component-configuration-type.util';
import { type FrontComponentConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/front-component-configuration.dto';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
export const validateFrontComponentFlatPageLayoutWidgetForCreation = (
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForCreationArgs,
): FlatPageLayoutWidgetValidationError[] => {
const { flatEntityToValidate } = args;
const { configuration, title } = flatEntityToValidate;
const { universalConfiguration, title } = flatEntityToValidate;
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration)) {
if (!isDefined(universalConfiguration)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration is required for front component widget "${title}"`,
@@ -24,13 +23,10 @@ export const validateFrontComponentFlatPageLayoutWidgetForCreation = (
return errors;
}
const frontComponentConfiguration =
configuration as FrontComponentConfigurationDTO;
const configurationTypeErrors = validateFrontComponentConfigurationType(
frontComponentConfiguration,
const configurationTypeErrors = validateFrontComponentConfigurationType({
universalConfiguration,
title,
);
});
errors.push(...configurationTypeErrors);
@@ -3,26 +3,22 @@ import { isDefined } from 'twenty-shared/utils';
import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs } from 'src/engine/metadata-modules/flat-page-layout-widget/services/flat-page-layout-widget-type-validator.service';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { validateFrontComponentConfigurationType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-front-component-configuration-type.util';
import { type FrontComponentConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/front-component-configuration.dto';
export const validateFrontComponentFlatPageLayoutWidgetForUpdate = (
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs,
): FlatPageLayoutWidgetValidationError[] => {
const { flatEntityToValidate } = args;
const { configuration, title } = flatEntityToValidate;
const { universalConfiguration, title } = flatEntityToValidate;
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration)) {
if (!isDefined(universalConfiguration)) {
return [];
}
const frontComponentConfiguration =
configuration as FrontComponentConfigurationDTO;
const configurationTypeErrors = validateFrontComponentConfigurationType(
frontComponentConfiguration,
const configurationTypeErrors = validateFrontComponentConfigurationType({
universalConfiguration,
title,
);
});
errors.push(...configurationTypeErrors);
@@ -1,23 +1,38 @@
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type GraphConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/graph-configuration.type';
import { validateBarChartConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-bar-chart-configuration.util';
import { validateLineChartConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-line-chart-configuration.util';
import { validatePieChartConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-pie-chart-configuration.util';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import {
type AllGraphWidgetConfigurationType,
WidgetConfigurationType,
} from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateGraphConfigurationByType = (
configuration: GraphConfiguration,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
const configurationType = configuration.configurationType;
export const validateGraphConfigurationByType = ({
graphUniversalConfiguration,
widgetTitle,
}: {
graphUniversalConfiguration: UniversalFlatPageLayoutWidget<AllGraphWidgetConfigurationType>['universalConfiguration'];
widgetTitle: string;
}): FlatPageLayoutWidgetValidationError[] => {
const configurationType = graphUniversalConfiguration.configurationType;
switch (configurationType) {
case WidgetConfigurationType.BAR_CHART:
return validateBarChartConfiguration(configuration, widgetTitle);
return validateBarChartConfiguration({
graphUniversalConfiguration,
widgetTitle,
});
case WidgetConfigurationType.LINE_CHART:
return validateLineChartConfiguration(configuration, widgetTitle);
return validateLineChartConfiguration({
graphUniversalConfiguration,
widgetTitle,
});
case WidgetConfigurationType.PIE_CHART:
return validatePieChartConfiguration(configuration, widgetTitle);
return validatePieChartConfiguration({
graphUniversalConfiguration,
widgetTitle,
});
case WidgetConfigurationType.AGGREGATE_CHART:
case WidgetConfigurationType.GAUGE_CHART:
return [];
@@ -3,42 +3,67 @@ import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { VALID_GRAPH_CONFIGURATION_TYPES } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/constants/valid-graph-configuration-types.constant';
import { type GraphConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/graph-configuration.type';
import { type AllGraphWidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateGraphConfigurationType = (
configuration: GraphConfiguration,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.configurationType)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration type is required for widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration type is required`,
});
return errors;
export const validateGraphConfigurationType = ({
universalConfiguration,
widgetTitle,
}: {
universalConfiguration: UniversalFlatPageLayoutWidget['universalConfiguration'];
widgetTitle: string;
}):
| {
status: 'fail';
errors: FlatPageLayoutWidgetValidationError[];
}
| {
status: 'success';
graphUniversalConfiguration: UniversalFlatPageLayoutWidget<AllGraphWidgetConfigurationType>['universalConfiguration'];
} => {
if (!isDefined(universalConfiguration.configurationType)) {
return {
errors: [
{
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration type is required for widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration type is required`,
},
],
status: 'fail',
};
}
const isValidGraphConfigurationType =
VALID_GRAPH_CONFIGURATION_TYPES.includes(configuration.configurationType);
VALID_GRAPH_CONFIGURATION_TYPES.includes(
universalConfiguration.configurationType as (typeof VALID_GRAPH_CONFIGURATION_TYPES)[number],
);
if (!isValidGraphConfigurationType) {
const expectedConfigurationTypes = VALID_GRAPH_CONFIGURATION_TYPES.map(
(type) => type.toString(),
).join(', ');
const configurationTypeString = configuration.configurationType.toString();
const configurationTypeString =
universalConfiguration.configurationType.toString();
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Invalid configuration type for graph widget "${widgetTitle}". Expected one of ${expectedConfigurationTypes}, got ${configurationTypeString}`,
userFriendlyMessage: msg`Invalid configuration type for graph widget`,
value: configuration.configurationType,
});
return {
errors: [
{
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Invalid configuration type for graph widget "${widgetTitle}". Expected one of ${expectedConfigurationTypes}, got ${configurationTypeString}`,
userFriendlyMessage: msg`Invalid configuration type for graph widget`,
value: universalConfiguration.configurationType,
},
],
status: 'fail',
};
}
return errors;
return {
status: 'success',
graphUniversalConfiguration:
universalConfiguration as UniversalFlatPageLayoutWidget<AllGraphWidgetConfigurationType>['universalConfiguration'],
};
};
@@ -3,7 +3,6 @@ import { isDefined } from 'twenty-shared/utils';
import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForCreationArgs } from 'src/engine/metadata-modules/flat-page-layout-widget/services/flat-page-layout-widget-type-validator.service';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type GraphConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/graph-configuration.type';
import { validateBaseGraphFields } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-base-graph-fields.util';
import { validateGraphConfigurationByType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-graph-configuration-by-type.util';
import { validateGraphConfigurationType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-graph-configuration-type.util';
@@ -13,40 +12,41 @@ export const validateGraphFlatPageLayoutWidgetForCreation = (
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForCreationArgs,
): FlatPageLayoutWidgetValidationError[] => {
const { flatEntityToValidate } = args;
const { configuration, title } = flatEntityToValidate;
const { universalConfiguration, title: widgetTitle } = flatEntityToValidate;
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration)) {
if (!isDefined(universalConfiguration)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration is required for graph widget "${title}"`,
message: t`Configuration is required for graph widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration is required for graph widget`,
});
return errors;
}
const graphConfiguration = configuration as GraphConfiguration;
const result = validateGraphConfigurationType({
universalConfiguration,
widgetTitle,
});
const configurationTypeErrors = validateGraphConfigurationType(
graphConfiguration,
title,
);
errors.push(...configurationTypeErrors);
if (configurationTypeErrors.length > 0) {
return errors;
if (result.status === 'fail') {
return [...errors, ...result.errors];
}
const baseFieldErrors = validateBaseGraphFields(graphConfiguration, title);
const { graphUniversalConfiguration } = result;
const baseFieldErrors = validateBaseGraphFields({
graphUniversalConfiguration,
widgetTitle,
});
errors.push(...baseFieldErrors);
const typeSpecificErrors = validateGraphConfigurationByType(
graphConfiguration,
title,
);
const typeSpecificErrors = validateGraphConfigurationByType({
graphUniversalConfiguration,
widgetTitle,
});
errors.push(...typeSpecificErrors);
@@ -2,7 +2,6 @@ import { isDefined } from 'twenty-shared/utils';
import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs } from 'src/engine/metadata-modules/flat-page-layout-widget/services/flat-page-layout-widget-type-validator.service';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type GraphConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/graph-configuration.type';
import { validateBaseGraphFields } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-base-graph-fields.util';
import { validateGraphConfigurationByType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-graph-configuration-by-type.util';
import { validateGraphConfigurationType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-graph-configuration-type.util';
@@ -11,34 +10,34 @@ export const validateGraphFlatPageLayoutWidgetForUpdate = (
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs,
): FlatPageLayoutWidgetValidationError[] => {
const { flatEntityToValidate } = args;
const { configuration, title } = flatEntityToValidate;
const { universalConfiguration, title: widgetTitle } = flatEntityToValidate;
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration)) {
if (!isDefined(universalConfiguration)) {
return [];
}
const graphConfiguration = configuration as GraphConfiguration;
const result = validateGraphConfigurationType({
universalConfiguration,
widgetTitle,
});
const configurationTypeErrors = validateGraphConfigurationType(
graphConfiguration,
title,
);
errors.push(...configurationTypeErrors);
if (configurationTypeErrors.length > 0) {
return errors;
if (result.status === 'fail') {
return result.errors;
}
const { graphUniversalConfiguration } = result;
const baseFieldErrors = validateBaseGraphFields(graphConfiguration, title);
const baseFieldErrors = validateBaseGraphFields({
graphUniversalConfiguration,
widgetTitle,
});
errors.push(...baseFieldErrors);
const typeSpecificErrors = validateGraphConfigurationByType(
graphConfiguration,
title,
);
const typeSpecificErrors = validateGraphConfigurationByType({
graphUniversalConfiguration,
widgetTitle,
});
errors.push(...typeSpecificErrors);
@@ -2,36 +2,60 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type IframeConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/iframe-configuration.type';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateIframeConfigurationType = (
configuration: IframeConfiguration,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.configurationType)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration type is required for widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration type is required`,
});
return errors;
export const validateIframeConfigurationType = ({
universalConfiguration,
widgetTitle,
}: {
universalConfiguration: UniversalFlatPageLayoutWidget['universalConfiguration'];
widgetTitle: string;
}):
| {
status: 'fail';
errors: FlatPageLayoutWidgetValidationError[];
}
| {
status: 'success';
iframeUniversalConfiguration: UniversalFlatPageLayoutWidget<WidgetConfigurationType.IFRAME>['universalConfiguration'];
} => {
if (!isDefined(universalConfiguration.configurationType)) {
return {
errors: [
{
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration type is required for widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration type is required`,
},
],
status: 'fail',
};
}
if (configuration.configurationType !== WidgetConfigurationType.IFRAME) {
const configurationTypeString = configuration.configurationType.toString();
if (
universalConfiguration.configurationType !== WidgetConfigurationType.IFRAME
) {
const configurationTypeString =
universalConfiguration.configurationType.toString();
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Invalid configuration type for iframe widget "${widgetTitle}". Expected IFRAME, got ${configurationTypeString}`,
userFriendlyMessage: msg`Invalid configuration type for iframe widget`,
value: configuration.configurationType,
});
return {
errors: [
{
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Invalid configuration type for iframe widget "${widgetTitle}". Expected IFRAME, got ${configurationTypeString}`,
userFriendlyMessage: msg`Invalid configuration type for iframe widget`,
value: universalConfiguration.configurationType,
},
],
status: 'fail',
};
}
return errors;
return {
status: 'success',
iframeUniversalConfiguration:
universalConfiguration as UniversalFlatPageLayoutWidget<WidgetConfigurationType.IFRAME>['universalConfiguration'],
};
};
@@ -3,7 +3,6 @@ import { isDefined } from 'twenty-shared/utils';
import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForCreationArgs } from 'src/engine/metadata-modules/flat-page-layout-widget/services/flat-page-layout-widget-type-validator.service';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type IframeConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/iframe-configuration.type';
import { validateIframeConfigurationType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-iframe-configuration-type.util';
import { validateIframeUrl } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-iframe-url.util';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
@@ -12,29 +11,34 @@ export const validateIframeFlatPageLayoutWidgetForCreation = (
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForCreationArgs,
): FlatPageLayoutWidgetValidationError[] => {
const { flatEntityToValidate } = args;
const { configuration, title } = flatEntityToValidate;
const { universalConfiguration, title: widgetTitle } = flatEntityToValidate;
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration)) {
if (!isDefined(universalConfiguration)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration is required for iframe widget "${title}"`,
message: t`Configuration is required for iframe widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration is required for iframe widget`,
});
return errors;
}
const iframeConfiguration = configuration as IframeConfiguration;
const result = validateIframeConfigurationType({
universalConfiguration,
widgetTitle,
});
const configurationTypeErrors = validateIframeConfigurationType(
iframeConfiguration,
title,
);
if (result.status === 'fail') {
return [...errors, ...result.errors];
}
errors.push(...configurationTypeErrors);
const { iframeUniversalConfiguration } = result;
const urlErrors = validateIframeUrl(iframeConfiguration, title);
const urlErrors = validateIframeUrl({
iframeUniversalConfiguration,
widgetTitle,
});
errors.push(...urlErrors);
@@ -2,7 +2,6 @@ import { isDefined } from 'twenty-shared/utils';
import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs } from 'src/engine/metadata-modules/flat-page-layout-widget/services/flat-page-layout-widget-type-validator.service';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type IframeConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/iframe-configuration.type';
import { validateIframeConfigurationType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-iframe-configuration-type.util';
import { validateIframeUrl } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-iframe-url.util';
@@ -10,23 +9,28 @@ export const validateIframeFlatPageLayoutWidgetForUpdate = (
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs,
): FlatPageLayoutWidgetValidationError[] => {
const { flatEntityToValidate } = args;
const { configuration, title } = flatEntityToValidate;
const { universalConfiguration, title: widgetTitle } = flatEntityToValidate;
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration)) {
if (!isDefined(universalConfiguration)) {
return [];
}
const iframeConfiguration = configuration as IframeConfiguration;
const result = validateIframeConfigurationType({
universalConfiguration,
widgetTitle,
});
const configurationTypeErrors = validateIframeConfigurationType(
iframeConfiguration,
title,
);
if (result.status === 'fail') {
return result.errors;
}
errors.push(...configurationTypeErrors);
const { iframeUniversalConfiguration } = result;
const urlErrors = validateIframeUrl(iframeConfiguration, title);
const urlErrors = validateIframeUrl({
iframeUniversalConfiguration,
widgetTitle,
});
errors.push(...urlErrors);
@@ -2,21 +2,28 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type IframeConfiguration } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/types/iframe-configuration.type';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateIframeUrl = (
configuration: IframeConfiguration,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
export const validateIframeUrl = ({
iframeUniversalConfiguration,
widgetTitle,
}: {
iframeUniversalConfiguration: UniversalFlatPageLayoutWidget<WidgetConfigurationType.IFRAME>['universalConfiguration'];
widgetTitle: string;
}): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (isDefined(configuration.url) && typeof configuration.url !== 'string') {
if (
isDefined(iframeUniversalConfiguration.url) &&
typeof iframeUniversalConfiguration.url !== 'string'
) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`URL must be a string for widget "${widgetTitle}"`,
userFriendlyMessage: msg`URL must be a string`,
value: configuration.url,
value: iframeUniversalConfiguration.url,
});
}
@@ -2,16 +2,24 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type LineChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateLineChartConfiguration = (
configuration: LineChartConfigurationDTO,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
export const validateLineChartConfiguration = ({
graphUniversalConfiguration,
widgetTitle,
}: {
graphUniversalConfiguration: UniversalFlatPageLayoutWidget<WidgetConfigurationType.LINE_CHART>['universalConfiguration'];
widgetTitle: string;
}): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.primaryAxisGroupByFieldMetadataId)) {
if (
!isDefined(
graphUniversalConfiguration.primaryAxisGroupByFieldMetadataUniversalIdentifier,
)
) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Primary axis group by field is required for line chart widget "${widgetTitle}"`,
@@ -2,16 +2,24 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type PieChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validatePieChartConfiguration = (
configuration: PieChartConfigurationDTO,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
export const validatePieChartConfiguration = ({
graphUniversalConfiguration,
widgetTitle,
}: {
graphUniversalConfiguration: UniversalFlatPageLayoutWidget<WidgetConfigurationType.PIE_CHART>['universalConfiguration'];
widgetTitle: string;
}): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.groupByFieldMetadataId)) {
if (
!isDefined(
graphUniversalConfiguration.groupByFieldMetadataUniversalIdentifier,
)
) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Group by field is required for pie chart widget "${widgetTitle}"`,
@@ -2,16 +2,20 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto';
import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateStandaloneRichTextBody = (
configuration: StandaloneRichTextConfigurationDTO,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
export const validateStandaloneRichTextBody = ({
standaloneRichTextUniversalConfiguration,
widgetTitle,
}: {
standaloneRichTextUniversalConfiguration: UniversalFlatPageLayoutWidget<WidgetConfigurationType.STANDALONE_RICH_TEXT>['universalConfiguration'];
widgetTitle: string;
}): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.body)) {
if (!isDefined(standaloneRichTextUniversalConfiguration.body)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Body is required for standalone rich text widget "${widgetTitle}"`,
@@ -22,14 +26,14 @@ export const validateStandaloneRichTextBody = (
}
if (
typeof configuration.body !== 'object' ||
Array.isArray(configuration.body)
typeof standaloneRichTextUniversalConfiguration.body !== 'object' ||
Array.isArray(standaloneRichTextUniversalConfiguration.body)
) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Body must be an object for widget "${widgetTitle}"`,
userFriendlyMessage: msg`Body must be an object`,
value: configuration.body,
value: standaloneRichTextUniversalConfiguration.body,
});
}
@@ -2,42 +2,61 @@ import { msg, t } from '@lingui/core/macro';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { type StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const validateStandaloneRichTextConfigurationType = (
configuration: StandaloneRichTextConfigurationDTO,
widgetTitle: string,
): FlatPageLayoutWidgetValidationError[] => {
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration.configurationType)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration type is required for widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration type is required`,
});
return errors;
export const validateStandaloneRichTextConfigurationType = ({
universalConfiguration,
widgetTitle,
}: {
universalConfiguration: UniversalFlatPageLayoutWidget['universalConfiguration'];
widgetTitle: string;
}):
| {
status: 'fail';
errors: FlatPageLayoutWidgetValidationError[];
}
| {
status: 'success';
standaloneRichTextUniversalConfiguration: UniversalFlatPageLayoutWidget<WidgetConfigurationType.STANDALONE_RICH_TEXT>['universalConfiguration'];
} => {
if (!isDefined(universalConfiguration.configurationType)) {
return {
errors: [
{
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration type is required for widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration type is required`,
},
],
status: 'fail',
};
}
if (
configuration.configurationType !==
universalConfiguration.configurationType !==
WidgetConfigurationType.STANDALONE_RICH_TEXT
) {
const expectedConfigurationType =
WidgetConfigurationType.STANDALONE_RICH_TEXT;
const configurationTypeString =
universalConfiguration.configurationType.toString();
const configurationType = configuration.configurationType;
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Invalid configuration type for standalone rich text widget "${widgetTitle}". Expected ${expectedConfigurationType}, got ${configurationType}`,
userFriendlyMessage: msg`Invalid configuration type for standalone rich text widget`,
value: configuration.configurationType,
});
return {
errors: [
{
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Invalid configuration type for standalone rich text widget "${widgetTitle}". Expected STANDALONE_RICH_TEXT, got ${configurationTypeString}`,
userFriendlyMessage: msg`Invalid configuration type for standalone rich text widget`,
value: universalConfiguration.configurationType,
},
],
status: 'fail',
};
}
return errors;
return {
status: 'success',
standaloneRichTextUniversalConfiguration:
universalConfiguration as UniversalFlatPageLayoutWidget<WidgetConfigurationType.STANDALONE_RICH_TEXT>['universalConfiguration'],
};
};
@@ -5,40 +5,40 @@ import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForCreationArgs } fro
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { validateStandaloneRichTextBody } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-standalone-rich-text-body.util';
import { validateStandaloneRichTextConfigurationType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-standalone-rich-text-configuration-type.util';
import { type StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
export const validateStandaloneRichTextFlatPageLayoutWidgetForCreation = (
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForCreationArgs,
): FlatPageLayoutWidgetValidationError[] => {
const { flatEntityToValidate } = args;
const { configuration, title } = flatEntityToValidate;
const { universalConfiguration, title: widgetTitle } = flatEntityToValidate;
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration)) {
if (!isDefined(universalConfiguration)) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Configuration is required for standalone rich text widget "${title}"`,
message: t`Configuration is required for standalone rich text widget "${widgetTitle}"`,
userFriendlyMessage: msg`Configuration is required for standalone rich text widget`,
});
return errors;
}
const standaloneRichTextConfiguration =
configuration as StandaloneRichTextConfigurationDTO;
const result = validateStandaloneRichTextConfigurationType({
universalConfiguration,
widgetTitle,
});
const configurationTypeErrors = validateStandaloneRichTextConfigurationType(
standaloneRichTextConfiguration,
title,
);
if (result.status === 'fail') {
return [...errors, ...result.errors];
}
errors.push(...configurationTypeErrors);
const { standaloneRichTextUniversalConfiguration } = result;
const bodyErrors = validateStandaloneRichTextBody(
standaloneRichTextConfiguration,
title,
);
const bodyErrors = validateStandaloneRichTextBody({
standaloneRichTextUniversalConfiguration,
widgetTitle,
});
errors.push(...bodyErrors);
@@ -4,33 +4,33 @@ import { type ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs } from
import { type FlatPageLayoutWidgetValidationError } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-validation-error.type';
import { validateStandaloneRichTextBody } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-standalone-rich-text-body.util';
import { validateStandaloneRichTextConfigurationType } from 'src/engine/metadata-modules/flat-page-layout-widget/validators/utils/validate-standalone-rich-text-configuration-type.util';
import { type StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto';
export const validateStandaloneRichTextFlatPageLayoutWidgetForUpdate = (
args: ValidateFlatPageLayoutWidgetTypeSpecificitiesForUpdateArgs,
): FlatPageLayoutWidgetValidationError[] => {
const { flatEntityToValidate } = args;
const { configuration, title } = flatEntityToValidate;
const { universalConfiguration, title: widgetTitle } = flatEntityToValidate;
const errors: FlatPageLayoutWidgetValidationError[] = [];
if (!isDefined(configuration)) {
if (!isDefined(universalConfiguration)) {
return [];
}
const standaloneRichTextConfiguration =
configuration as StandaloneRichTextConfigurationDTO;
const result = validateStandaloneRichTextConfigurationType({
universalConfiguration,
widgetTitle,
});
const configurationTypeErrors = validateStandaloneRichTextConfigurationType(
standaloneRichTextConfiguration,
title,
);
if (result.status === 'fail') {
return result.errors;
}
errors.push(...configurationTypeErrors);
const { standaloneRichTextUniversalConfiguration } = result;
const bodyErrors = validateStandaloneRichTextBody(
standaloneRichTextConfiguration,
title,
);
const bodyErrors = validateStandaloneRichTextBody({
standaloneRichTextUniversalConfiguration,
widgetTitle,
});
errors.push(...bodyErrors);