Share RICH_TEXT_V2 field value override between REST and GraphQL APIs (#10912)
Fixes issue #10606. This PR makes `RICH_TEXT_V2` field behavior in REST API matche the current behavior in GraphQL API: Currently both `markdown` and `blocknote` fields must be included in the request, one of them can be `null`. The field with a `null` value will be filled by the converted value of the other field. In other words, this works: ``` curl http://localhost:3000/rest/notes \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzQxODA1MzQyLCJleHAiOjQ4OTU0MDUzNDEsImp0aSI6ImZlMzU0NTBkLTlhMDMtNGE2ZS04ODVjLTBlNTU3M2Y3YTE0NiJ9.6_g8cwoSE7ZCX1Zzsw44gZIyBdLKNsnDqMOmm1bKik0' \ --data '{ "position": 1, "title": "a", "bodyV2": { "markdown": "test4\n\ntest3\n\n# test1\n", "blocknote": null }, "createdBy": { "source": "EMAIL" } }' ``` And this does not work: ``` curl http://localhost:3000/rest/notes \ --request POST \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC0xYzI1LTRkMDItYmYyNS02YWVjY2Y3ZWE0MTkiLCJ0eXBlIjoiQVBJX0tFWSIsIndvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWMyNS00ZDAyLWJmMjUtNmFlY2NmN2VhNDE5IiwiaWF0IjoxNzQxODA1MzQyLCJleHAiOjQ4OTU0MDUzNDEsImp0aSI6ImZlMzU0NTBkLTlhMDMtNGE2ZS04ODVjLTBlNTU3M2Y3YTE0NiJ9.6_g8cwoSE7ZCX1Zzsw44gZIyBdLKNsnDqMOmm1bKik0' \ --data '{ "position": 1, "title": "", "body": "", "bodyV2": { "markdown": "test4\n\ntest3\n\n# test1\n" }, "createdBy": { "source": "EMAIL" } }' ``` --- It would be nice not to require the null value, maybe let's make that a separate PR?
This commit is contained in:
@ -1,13 +1,14 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { Request } from 'express';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { Request } from 'express';
|
||||
|
||||
import { ObjectRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/object-record.interface';
|
||||
|
||||
import { CoreQueryBuilderFactory } from 'src/engine/api/rest/core/query-builder/core-query-builder.factory';
|
||||
import { parseCorePath } from 'src/engine/api/rest/core/query-builder/utils/path-parsers/parse-core-path.utils';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { RecordInputTransformerService } from 'src/engine/core-modules/record-transformer/services/record-input-transformer.service';
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
|
||||
@ -16,7 +17,7 @@ export class RestApiCoreServiceV2 {
|
||||
constructor(
|
||||
private readonly coreQueryBuilderFactory: CoreQueryBuilderFactory,
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
|
||||
private readonly recordInputTransformerService: RecordInputTransformerService,
|
||||
protected readonly apiEventEmitterService: ApiEventEmitterService,
|
||||
) {}
|
||||
|
||||
@ -47,11 +48,15 @@ export class RestApiCoreServiceV2 {
|
||||
}
|
||||
|
||||
async createOne(request: Request) {
|
||||
const { body } = request;
|
||||
|
||||
const { objectMetadataNameSingular, objectMetadata, repository } =
|
||||
await this.getRepositoryAndMetadataOrFail(request);
|
||||
const createdRecord = await repository.save(body);
|
||||
|
||||
const overriddenBody = await this.recordInputTransformerService.process({
|
||||
recordInput: request.body,
|
||||
objectMetadataMapItem: objectMetadata.objectMetadataMapItem,
|
||||
});
|
||||
|
||||
const createdRecord = await repository.save(overriddenBody);
|
||||
|
||||
this.apiEventEmitterService.emitCreateEvents(
|
||||
[createdRecord],
|
||||
@ -80,9 +85,14 @@ export class RestApiCoreServiceV2 {
|
||||
where: { id: recordId },
|
||||
});
|
||||
|
||||
const overriddenBody = await this.recordInputTransformerService.process({
|
||||
recordInput: request.body,
|
||||
objectMetadataMapItem: objectMetadata.objectMetadataMapItem,
|
||||
});
|
||||
|
||||
const updatedRecord = await repository.save({
|
||||
...recordToUpdate,
|
||||
...request.body,
|
||||
...overriddenBody,
|
||||
});
|
||||
|
||||
this.apiEventEmitterService.emitUpdateEvents(
|
||||
@ -139,7 +149,11 @@ export class RestApiCoreServiceV2 {
|
||||
objectMetadataNameSingular,
|
||||
);
|
||||
|
||||
return { objectMetadataNameSingular, objectMetadata, repository };
|
||||
return {
|
||||
objectMetadataNameSingular,
|
||||
objectMetadata,
|
||||
repository,
|
||||
};
|
||||
}
|
||||
|
||||
private getAuthContextFromRequest(request: Request): AuthContext {
|
||||
|
||||
@ -14,6 +14,7 @@ import { RestApiMetadataController } from 'src/engine/api/rest/metadata/rest-api
|
||||
import { RestApiMetadataService } from 'src/engine/api/rest/metadata/rest-api-metadata.service';
|
||||
import { RestApiService } from 'src/engine/api/rest/rest-api.service';
|
||||
import { AuthModule } from 'src/engine/core-modules/auth/auth.module';
|
||||
import { RecordTransformerModule } from 'src/engine/core-modules/record-transformer/record-transformer.module';
|
||||
import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-runner/services/api-event-emitter.service';
|
||||
@ -26,6 +27,7 @@ import { ApiEventEmitterService } from 'src/engine/api/graphql/graphql-query-run
|
||||
AuthModule,
|
||||
HttpModule,
|
||||
TwentyORMModule,
|
||||
RecordTransformerModule,
|
||||
],
|
||||
controllers: [
|
||||
RestApiMetadataController,
|
||||
|
||||
Reference in New Issue
Block a user