Fail slow + structured validator error response scoped by object and fields (#13980)
# Introduction
Example of returned response: ( snapshot copy don't mind jest expect any
values )
```json
{
"extensions": {
"code": "BAD_USER_INPUT",
"errors": {
"fieldMetadata": [
{
"errors": [
{
"code": "INVALID_FIELD_INPUT",
"message": "Name is too long",
"userFriendlyMessage": "Name is too long",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
],
"id": Any<String>,
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"objectMetadataId": Any<String>,
"operation": "create_field",
},
{
"errors": [
{
"code": "INVALID_FIELD_INPUT",
"message": "Name is too long",
"userFriendlyMessage": "Name is too long",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
],
"id": Any<String>,
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"objectMetadataId": Any<String>,
"operation": "create_field",
},
{
"errors": [
{
"code": "INVALID_FIELD_INPUT",
"message": "Name is too long",
"userFriendlyMessage": "Name is too long",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
],
"id": Any<String>,
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"objectMetadataId": Any<String>,
"operation": "create_field",
},
{
"errors": [
{
"code": "INVALID_FIELD_INPUT",
"message": "Name is too long",
"userFriendlyMessage": "Name is too long",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
],
"id": Any<String>,
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"objectMetadataId": Any<String>,
"operation": "create_field",
},
{
"errors": [
{
"code": "INVALID_FIELD_INPUT",
"message": "Name is too long",
"userFriendlyMessage": "Name is too long",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
],
"id": Any<String>,
"name": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"objectMetadataId": Any<String>,
"operation": "create_field",
},
],
"objectMetadata": [
{
"errors": [
{
"code": "INVALID_OBJECT_INPUT",
"message": "Name is too long",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
],
"fields": [],
"id": Any<String>,
"namePlural": "listingas",
"nameSingular": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"operation": "create_object",
},
],
},
"message": "Validation failed for 1 object(s) and 5 field(s)",
"summary": {
"invalidFields": 5,
"invalidObjects": 1,
"totalErrors": 6,
},
"userFriendlyMessage": "Validation failed for 1 object(s) and 5 field(s)",
},
"message": "Multiple validation errors occurred while creating object",
"name": "GraphQLError",
}
```
## Note
We should put in place integrations tests on REST API too, in order to
ensure we receive the same response error
This commit is contained in:
+11
@@ -0,0 +1,11 @@
|
||||
import { type FailedWorkspaceMigrationBuildResult } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
|
||||
export class WorkspaceMigrationBuilderExceptionV2 extends Error {
|
||||
constructor(
|
||||
public readonly failedWorkspaceMigrationBuildResult: FailedWorkspaceMigrationBuildResult,
|
||||
message = 'Workspace migration builder failed',
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'WorkspaceMigrationBuilderExceptionV2';
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
import { type FieldMetadataMinimalInformation } from 'src/engine/metadata-modules/flat-field-metadata/types/field-metadata-minimal-information.type';
|
||||
import { type FlatFieldMetadataValidationError } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata-validation-error.type';
|
||||
import { type FlatObjectMetadataValidationError } from 'src/engine/metadata-modules/flat-object-metadata/types/flat-object-metadata-validation-error.type';
|
||||
import { type ObjectMetadataMinimalInformation } from 'src/engine/metadata-modules/flat-object-metadata/types/object-metadata-minimal-information.type';
|
||||
import { type WorkspaceMigrationFieldActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-field-action-v2';
|
||||
import { type WorkspaceMigrationObjectActionTypeV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-object-action-v2';
|
||||
|
||||
export type ValidationErrorFieldResponse =
|
||||
Partial<FieldMetadataMinimalInformation> & {
|
||||
operation: WorkspaceMigrationFieldActionTypeV2;
|
||||
errors: FlatFieldMetadataValidationError[];
|
||||
};
|
||||
export type ValidationErrorObjectResponse =
|
||||
Partial<ObjectMetadataMinimalInformation> & {
|
||||
operation: WorkspaceMigrationObjectActionTypeV2;
|
||||
errors: FlatObjectMetadataValidationError[];
|
||||
fields: ValidationErrorFieldResponse[];
|
||||
};
|
||||
export type ValidationErrorResponse = {
|
||||
summary: {
|
||||
totalErrors: number;
|
||||
invalidFields: number;
|
||||
invalidObjects: number;
|
||||
};
|
||||
errors: {
|
||||
objectMetadata: ValidationErrorObjectResponse[];
|
||||
fieldMetadata: ValidationErrorFieldResponse[];
|
||||
};
|
||||
};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { type FailedFlatFieldMetadataValidation } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { type ValidationErrorFieldResponse } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/types/validate-error-response.type';
|
||||
|
||||
export const fromFailedFlatFieldMetadataValidationToValidationErrorFieldResponse =
|
||||
(
|
||||
failedFlatFieldMetadataValidation: FailedFlatFieldMetadataValidation,
|
||||
): ValidationErrorFieldResponse => {
|
||||
const { id, name, objectMetadataId } =
|
||||
failedFlatFieldMetadataValidation.fieldMinimalInformation;
|
||||
|
||||
return {
|
||||
operation: failedFlatFieldMetadataValidation.type,
|
||||
id,
|
||||
name,
|
||||
errors: failedFlatFieldMetadataValidation.errors,
|
||||
objectMetadataId,
|
||||
};
|
||||
};
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { type FailedFlatObjectMetadataValidation } from 'src/engine/metadata-modules/flat-object-metadata/types/failed-flat-object-metadata-validation.type';
|
||||
import { type ValidationErrorObjectResponse } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/types/validate-error-response.type';
|
||||
import { fromFailedFlatFieldMetadataValidationToValidationErrorFieldResponse } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/utils/from-failed-flat-field-metadata-validation-to-validation-error-field-response.util';
|
||||
|
||||
export const fromFailedFlatObjectMetadataValidationToValidationErrorObjectResponse =
|
||||
(
|
||||
failedFlatObjectMetadataValidation: FailedFlatObjectMetadataValidation,
|
||||
): ValidationErrorObjectResponse => {
|
||||
const { id, namePlural, nameSingular } =
|
||||
failedFlatObjectMetadataValidation.objectMinimalInformation;
|
||||
const fields = failedFlatObjectMetadataValidation.fieldLevelErrors.map(
|
||||
fromFailedFlatFieldMetadataValidationToValidationErrorFieldResponse,
|
||||
);
|
||||
const objectResponseError: ValidationErrorObjectResponse = {
|
||||
operation: failedFlatObjectMetadataValidation.type,
|
||||
fields,
|
||||
id,
|
||||
namePlural,
|
||||
nameSingular,
|
||||
errors: failedFlatObjectMetadataValidation.objectLevelErrors,
|
||||
};
|
||||
|
||||
return objectResponseError;
|
||||
};
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { isFailedFlatObjectMetadataValidation } from 'src/engine/metadata-modules/flat-object-metadata/utils/is-failed-flat-object-metadata-validation.util';
|
||||
import { type WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
|
||||
import { type ValidationErrorResponse } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/types/validate-error-response.type';
|
||||
import { fromFailedFlatFieldMetadataValidationToValidationErrorFieldResponse } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/utils/from-failed-flat-field-metadata-validation-to-validation-error-field-response.util';
|
||||
import { fromFailedFlatObjectMetadataValidationToValidationErrorObjectResponse } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/utils/from-failed-flat-object-metadata-validation-to-validation-error-object-response.util';
|
||||
|
||||
export const fromWorkspaceMigrationBuilderExceptionToValidationResponseError = (
|
||||
workspaceMigrationBuilderException: WorkspaceMigrationBuilderExceptionV2,
|
||||
) => {
|
||||
const emptyResponseError: ValidationErrorResponse = {
|
||||
summary: {
|
||||
invalidFields: 0,
|
||||
invalidObjects: 0,
|
||||
totalErrors: 0,
|
||||
},
|
||||
errors: {
|
||||
fieldMetadata: [],
|
||||
objectMetadata: [],
|
||||
},
|
||||
};
|
||||
|
||||
return workspaceMigrationBuilderException.failedWorkspaceMigrationBuildResult.errors.reduce<ValidationErrorResponse>(
|
||||
({ errors, summary }, failedValidationError) => {
|
||||
if (isFailedFlatObjectMetadataValidation(failedValidationError)) {
|
||||
const errorObjectResponse =
|
||||
fromFailedFlatObjectMetadataValidationToValidationErrorObjectResponse(
|
||||
failedValidationError,
|
||||
);
|
||||
|
||||
return {
|
||||
summary: {
|
||||
...summary,
|
||||
invalidFields:
|
||||
summary.invalidFields + errorObjectResponse.fields.length,
|
||||
invalidObjects: ++summary.invalidObjects,
|
||||
totalErrors:
|
||||
++summary.totalErrors + errorObjectResponse.fields.length,
|
||||
},
|
||||
errors: {
|
||||
...errors,
|
||||
objectMetadata: [...errors.objectMetadata, errorObjectResponse],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const fieldResponseError =
|
||||
fromFailedFlatFieldMetadataValidationToValidationErrorFieldResponse(
|
||||
failedValidationError,
|
||||
);
|
||||
|
||||
return {
|
||||
summary: {
|
||||
...summary,
|
||||
invalidFields: ++summary.invalidFields,
|
||||
totalErrors: ++summary.totalErrors,
|
||||
},
|
||||
errors: {
|
||||
...errors,
|
||||
fieldMetadata: [...errors.fieldMetadata, fieldResponseError],
|
||||
},
|
||||
};
|
||||
},
|
||||
emptyResponseError,
|
||||
);
|
||||
};
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import {
|
||||
type CallHandler,
|
||||
type ExecutionContext,
|
||||
type NestInterceptor,
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { catchError, type Observable } from 'rxjs';
|
||||
|
||||
import {
|
||||
BaseGraphQLError,
|
||||
ErrorCode,
|
||||
} from 'src/engine/core-modules/graphql/utils/graphql-errors.util';
|
||||
import { WorkspaceMigrationBuilderExceptionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/exceptions/workspace-migration-builder-exception-v2';
|
||||
import { fromWorkspaceMigrationBuilderExceptionToValidationResponseError } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/utils/from-workspace-migration-builder-exception-to-validation-response-error.util';
|
||||
|
||||
export class WorkspaceMigrationBuilderExceptionV2Interceptor
|
||||
implements NestInterceptor
|
||||
{
|
||||
intercept(_: ExecutionContext, next: CallHandler): Observable<unknown> {
|
||||
return next.handle().pipe(
|
||||
catchError((error) => {
|
||||
if (!(error instanceof WorkspaceMigrationBuilderExceptionV2)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const { errors, summary } =
|
||||
fromWorkspaceMigrationBuilderExceptionToValidationResponseError(
|
||||
error,
|
||||
);
|
||||
|
||||
throw new BaseGraphQLError(error.message, ErrorCode.BAD_USER_INPUT, {
|
||||
code: 'METADATA_VALIDATION_ERROR',
|
||||
errors,
|
||||
summary,
|
||||
message: `Validation failed for ${summary.invalidObjects} object(s) and ${summary.invalidFields} field(s)`,
|
||||
userFriendlyMessage: t`Validation failed for ${summary.invalidObjects} object(s) and ${summary.invalidFields} field(s)`,
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
+5
-9
@@ -1,7 +1,7 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { MultipleMetadataValidationErrors } from 'src/engine/core-modules/error/multiple-metadata-validation-errors';
|
||||
import {
|
||||
FailedWorkspaceMigrationBuildResult,
|
||||
WorkspaceMigrationBuildArgs,
|
||||
WorkspaceMigrationBuilderV2Service,
|
||||
} from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-builder-v2.service';
|
||||
@@ -22,10 +22,9 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
private readonly workspaceMigrationRunnerV2Service: WorkspaceMigrationRunnerV2Service,
|
||||
) {}
|
||||
|
||||
public async validateBuildAndRunWorkspaceMigration({
|
||||
errorMessage,
|
||||
...builderArgs
|
||||
}: WorkspaceMigrationBuildArgs & { errorMessage: string }) {
|
||||
public async validateBuildAndRunWorkspaceMigration(
|
||||
builderArgs: WorkspaceMigrationBuildArgs,
|
||||
): Promise<FailedWorkspaceMigrationBuildResult | undefined> {
|
||||
const validateAndBuildResult = await this.workspaceMigrationBuilderV2Service
|
||||
.validateAndBuild(builderArgs)
|
||||
.catch((error) => {
|
||||
@@ -37,10 +36,7 @@ export class WorkspaceMigrationValidateBuildAndRunService {
|
||||
});
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new MultipleMetadataValidationErrors(
|
||||
validateAndBuildResult.errors,
|
||||
errorMessage,
|
||||
);
|
||||
return validateAndBuildResult;
|
||||
}
|
||||
|
||||
await this.workspaceMigrationRunnerV2Service
|
||||
|
||||
+17
-13
@@ -2,9 +2,9 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { type FromTo } from 'twenty-shared/types';
|
||||
|
||||
import { FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { FailedFlatFieldMetadataValidation } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { FailedFlatObjectMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-object-metadata/types/failed-flat-object-metadata-validation.type';
|
||||
import { FailedFlatObjectMetadataValidation } from 'src/engine/metadata-modules/flat-object-metadata/types/failed-flat-object-metadata-validation.type';
|
||||
import { fromFlatObjectMetadataMapsToFlatObjectMetadatas } from 'src/engine/metadata-modules/flat-object-metadata/utils/from-flat-object-metadata-maps-to-flat-object-metadatas.util';
|
||||
import { deletedCreatedUpdatedMatrixDispatcher } from 'src/engine/workspace-manager/workspace-migration-v2/utils/deleted-created-updated-matrix-dispatcher.util';
|
||||
import { WorkspaceMigrationV2FieldActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/services/workspace-migration-v2-field-actions-builder.service';
|
||||
@@ -20,6 +20,19 @@ export type WorkspaceMigrationV2BuilderOptions = {
|
||||
isSystemBuild: boolean;
|
||||
};
|
||||
|
||||
type SuccessfulWorkspaceMigrationBuildResult = {
|
||||
status: 'success';
|
||||
workspaceMigration: WorkspaceMigrationV2;
|
||||
};
|
||||
|
||||
export type FailedWorkspaceMigrationBuildResult = {
|
||||
status: 'fail';
|
||||
errors: (
|
||||
| FailedFlatObjectMetadataValidation
|
||||
| FailedFlatFieldMetadataValidation
|
||||
)[];
|
||||
};
|
||||
|
||||
export type WorkspaceMigrationBuildArgs = {
|
||||
workspaceId: string;
|
||||
buildOptions: WorkspaceMigrationV2BuilderOptions;
|
||||
@@ -37,17 +50,8 @@ export class WorkspaceMigrationBuilderV2Service {
|
||||
workspaceId,
|
||||
buildOptions,
|
||||
}: WorkspaceMigrationBuildArgs): Promise<
|
||||
| {
|
||||
status: 'fail';
|
||||
errors: (
|
||||
| FailedFlatObjectMetadataValidationExceptions
|
||||
| FailedFlatFieldMetadataValidationExceptions
|
||||
)[];
|
||||
}
|
||||
| {
|
||||
status: 'success';
|
||||
workspaceMigration: WorkspaceMigrationV2;
|
||||
}
|
||||
| SuccessfulWorkspaceMigrationBuildResult
|
||||
| FailedWorkspaceMigrationBuildResult
|
||||
> {
|
||||
const fromFlatObjectMetadatas =
|
||||
fromFlatObjectMetadataMapsToFlatObjectMetadatas(
|
||||
|
||||
+6
-6
@@ -93,8 +93,8 @@ export class WorkspaceMigrationV2FieldActionsBuilderService {
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(...validationErrors);
|
||||
if (validationErrors.errors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -142,8 +142,8 @@ export class WorkspaceMigrationV2FieldActionsBuilderService {
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(...validationErrors);
|
||||
if (validationErrors.errors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -174,8 +174,8 @@ export class WorkspaceMigrationV2FieldActionsBuilderService {
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(...validationErrors);
|
||||
if (validationErrors.errors.length > 0) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+15
-6
@@ -72,8 +72,11 @@ export class WorkspaceMigrationV2ObjectActionsBuilderService {
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(...validationErrors);
|
||||
if (
|
||||
validationErrors.fieldLevelErrors.length > 0 ||
|
||||
validationErrors.objectLevelErrors.length > 0
|
||||
) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -113,8 +116,11 @@ export class WorkspaceMigrationV2ObjectActionsBuilderService {
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(...validationErrors);
|
||||
if (
|
||||
validationErrors.fieldLevelErrors.length > 0 ||
|
||||
validationErrors.objectLevelErrors.length > 0
|
||||
) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -154,8 +160,11 @@ export class WorkspaceMigrationV2ObjectActionsBuilderService {
|
||||
},
|
||||
);
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
validateAndBuildResult.failed.push(...validationErrors);
|
||||
if (
|
||||
validationErrors.fieldLevelErrors.length > 0 ||
|
||||
validationErrors.objectLevelErrors.length > 0
|
||||
) {
|
||||
validateAndBuildResult.failed.push(validationErrors);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -1,14 +1,14 @@
|
||||
import { type FailedFlatFieldMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { type FailedFlatFieldMetadataValidation } from 'src/engine/metadata-modules/flat-field-metadata/types/failed-flat-field-metadata-validation.type';
|
||||
import { type FlatObjectMetadataMaps } from 'src/engine/metadata-modules/flat-object-metadata-maps/types/flat-object-metadata-maps.type';
|
||||
import { type FailedFlatObjectMetadataValidationExceptions } from 'src/engine/metadata-modules/flat-object-metadata/types/failed-flat-object-metadata-validation.type';
|
||||
import { type FailedFlatObjectMetadataValidation } from 'src/engine/metadata-modules/flat-object-metadata/types/failed-flat-object-metadata-validation.type';
|
||||
import { type WorkspaceMigrationActionV2 } from 'src/engine/workspace-manager/workspace-migration-v2/workspace-migration-builder-v2/types/workspace-migration-action-common-v2';
|
||||
|
||||
export type ValidateAndBuildMetadataResult<
|
||||
T extends WorkspaceMigrationActionV2,
|
||||
> = {
|
||||
failed: (
|
||||
| FailedFlatFieldMetadataValidationExceptions
|
||||
| FailedFlatObjectMetadataValidationExceptions
|
||||
| FailedFlatFieldMetadataValidation
|
||||
| FailedFlatObjectMetadataValidation
|
||||
)[];
|
||||
created: T[];
|
||||
deleted: T[];
|
||||
|
||||
+6
@@ -35,3 +35,9 @@ export type WorkspaceMigrationObjectActionV2 =
|
||||
|
||||
export type WorkspaceMigrationObjectActionTypeV2 =
|
||||
WorkspaceMigrationObjectActionV2['type'];
|
||||
|
||||
export const WORKSPACE_MIGRATION_OBJECT_ACTION_TYPES = [
|
||||
'create_object',
|
||||
'delete_object',
|
||||
'update_object',
|
||||
] as const satisfies WorkspaceMigrationObjectActionTypeV2[];
|
||||
|
||||
Reference in New Issue
Block a user