[3/n]: Migrate the PUT rest/* and PATCH rest/* to use TwentyORM (#10002)
# This PR - Is addressing #3644 - Migrates the PUT and PATCH rest/* endpoints to use twentyORM directly - Adds integration tests
This commit is contained in:
@ -34,7 +34,12 @@ import { CoreEngineModule } from './engine/core-modules/core-engine.module';
|
||||
import { I18nModule } from './engine/core-modules/i18n/i18n.module';
|
||||
|
||||
// TODO: Remove this middleware when all the rest endpoints are migrated to TwentyORM
|
||||
const MIGRATED_REST_METHODS = [RequestMethod.DELETE];
|
||||
const MIGRATED_REST_METHODS = [
|
||||
RequestMethod.DELETE,
|
||||
RequestMethod.POST,
|
||||
RequestMethod.PATCH,
|
||||
RequestMethod.PUT,
|
||||
];
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
||||
@ -61,19 +61,21 @@ export class RestApiCoreController {
|
||||
}
|
||||
|
||||
@Patch()
|
||||
@UseFilters(RestApiExceptionFilter)
|
||||
async handleApiPatch(@Req() request: Request, @Res() res: Response) {
|
||||
const result = await this.restApiCoreService.update(request);
|
||||
const result = await this.restApiCoreServiceV2.update(request);
|
||||
|
||||
res.status(200).send(cleanGraphQLResponse(result.data.data));
|
||||
res.status(200).send(result);
|
||||
}
|
||||
|
||||
// This endpoint is not documented in the OpenAPI schema.
|
||||
// We keep it to avoid a breaking change since it initially used PUT instead of PATCH,
|
||||
// and because the PUT verb is often used as a PATCH.
|
||||
@Put()
|
||||
@UseFilters(RestApiExceptionFilter)
|
||||
async handleApiPut(@Req() request: Request, @Res() res: Response) {
|
||||
const result = await this.restApiCoreService.update(request);
|
||||
const result = await this.restApiCoreServiceV2.update(request);
|
||||
|
||||
res.status(200).send(cleanGraphQLResponse(result.data.data));
|
||||
res.status(200).send(result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,6 +48,32 @@ export class RestApiCoreServiceV2 {
|
||||
);
|
||||
}
|
||||
|
||||
async update(request: Request) {
|
||||
const { id: recordId } = parseCorePath(request);
|
||||
|
||||
if (!recordId) {
|
||||
throw new BadRequestException('Record ID not found');
|
||||
}
|
||||
|
||||
const { objectMetadataNameSingular, repository } =
|
||||
await this.getRepositoryAndMetadataOrFail(request);
|
||||
|
||||
const recordToUpdate = await repository.findOneOrFail({
|
||||
where: { id: recordId },
|
||||
});
|
||||
|
||||
const updatedRecord = await repository.save({
|
||||
...recordToUpdate,
|
||||
...request.body,
|
||||
});
|
||||
|
||||
return this.formatResult(
|
||||
'update',
|
||||
objectMetadataNameSingular,
|
||||
updatedRecord,
|
||||
);
|
||||
}
|
||||
|
||||
private formatResult<T>(
|
||||
operation: 'delete' | 'create' | 'update' | 'find',
|
||||
objectNameSingular: string,
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
import { PERSON_2_ID } from 'test/integration/constants/mock-person-ids.constants';
|
||||
|
||||
export const INITIAL_PERSON_DATA = {
|
||||
id: PERSON_2_ID,
|
||||
name: {
|
||||
firstName: 'Testing',
|
||||
lastName: 'User',
|
||||
},
|
||||
emails: {
|
||||
primaryEmail: 'test8@user.com',
|
||||
additionalEmails: ['user8@example.com'],
|
||||
},
|
||||
city: 'New York',
|
||||
jobTitle: 'Manager',
|
||||
companyId: '20202020-0713-40a5-8216-82802401d33e',
|
||||
};
|
||||
@ -0,0 +1,90 @@
|
||||
import { INITIAL_PERSON_DATA } from 'test/integration/constants/initial-person-data.constants';
|
||||
import {
|
||||
FAKE_PERSON_ID,
|
||||
PERSON_2_ID,
|
||||
} from 'test/integration/constants/mock-person-ids.constants';
|
||||
import { makeRestAPIRequest } from 'test/integration/rest/utils/make-rest-api-request.util';
|
||||
import { generateRecordName } from 'test/integration/utils/generate-record-name';
|
||||
|
||||
describe('Core REST API Update One endpoint', () => {
|
||||
let initialPersonData;
|
||||
|
||||
beforeAll(async () => {
|
||||
initialPersonData = INITIAL_PERSON_DATA;
|
||||
|
||||
await makeRestAPIRequest({
|
||||
method: 'post',
|
||||
path: `/people`,
|
||||
body: initialPersonData,
|
||||
}).expect(200);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await makeRestAPIRequest({
|
||||
method: 'delete',
|
||||
path: `/people/${PERSON_2_ID}`,
|
||||
}).expect(200);
|
||||
});
|
||||
|
||||
it('3.a. should update an existing person (name, emails, and city)', async () => {
|
||||
const updatedData = {
|
||||
name: {
|
||||
firstName: 'Updated',
|
||||
lastName: 'Person',
|
||||
},
|
||||
emails: {
|
||||
primaryEmail: 'updated@example.com',
|
||||
additionalEmails: ['extra@example.com'],
|
||||
},
|
||||
city: generateRecordName(PERSON_2_ID),
|
||||
};
|
||||
|
||||
const response = await makeRestAPIRequest({
|
||||
method: 'patch',
|
||||
path: `/people/${PERSON_2_ID}`,
|
||||
body: updatedData,
|
||||
}).expect(200);
|
||||
|
||||
const updatedPerson = response.body.data.updatePerson;
|
||||
|
||||
expect(updatedPerson.id).toBe(PERSON_2_ID);
|
||||
expect(updatedPerson.name.firstName).toBe(updatedData.name.firstName);
|
||||
expect(updatedPerson.name.lastName).toBe(updatedData.name.lastName);
|
||||
expect(updatedPerson.emails.primaryEmail).toBe(
|
||||
updatedData.emails.primaryEmail,
|
||||
);
|
||||
expect(updatedPerson.emails.additionalEmails).toEqual(
|
||||
updatedData.emails.additionalEmails,
|
||||
);
|
||||
expect(updatedPerson.city).toBe(updatedData.city);
|
||||
|
||||
expect(updatedPerson.jobTitle).toBe(initialPersonData.jobTitle);
|
||||
expect(updatedPerson.companyId).toBe(initialPersonData.companyId);
|
||||
});
|
||||
|
||||
it('3.b. should return a BadRequestException when trying to update a non-existing person', async () => {
|
||||
await makeRestAPIRequest({
|
||||
method: 'patch',
|
||||
path: `/people/${FAKE_PERSON_ID}`,
|
||||
body: { city: 'NonExistingCity' },
|
||||
})
|
||||
.expect(400)
|
||||
.expect((res) => {
|
||||
expect(res.body.error).toBe('BadRequestException');
|
||||
expect(res.body.messages[0]).toContain('Record ID not found');
|
||||
});
|
||||
});
|
||||
|
||||
it('3.c. should return an UnauthorizedException when an invalid token is provided', async () => {
|
||||
await makeRestAPIRequest({
|
||||
method: 'patch',
|
||||
path: `/people/${PERSON_2_ID}`,
|
||||
headers: { authorization: 'Bearer invalid-token' },
|
||||
body: { city: 'InvalidTokenCity' },
|
||||
})
|
||||
.expect(401)
|
||||
.expect((res) => {
|
||||
expect(res.body.error).toBe('UNAUTHENTICATED');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user