Refactor seed to use twenty-standard application (#16598)
# Introduction In this pull-request we introduce a service dedicated to the twenty-standard app installation, we will later be able to re-use existing logic to be more generic and allow any app installation. For the moment sticking to this usage https://github.com/twentyhq/core-team-issues/issues/1995 ## Encountered issues - We decided not to migrate deprecated fields ( also they will become custom field for any existing workspace having them in the future ) - duplicate criteria - wrong search index declaration - forgotten isSearchable - Attachement seed - Restored standardId ## Note For the moment we're still searching through standardId for code that run on both existing and new workspaces. For code running on new workspace exclusively we're searching using universalIdentifier We will standardize universalIdentifier usage later when we've migratred all the existing workspaces ## Workspace creation Will handle workspace creation the same way in another PR Related https://github.com/twentyhq/twenty/pull/15065 ## TODO - [ ] Double all frontend hardcoded queries to not refer to deprecated fields especially attachments
This commit is contained in:
-3
@@ -12,12 +12,10 @@ describe('attachmentsResolver (e2e)', () => {
|
||||
node {
|
||||
name
|
||||
fullPath
|
||||
type
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
deletedAt
|
||||
authorId
|
||||
taskId
|
||||
noteId
|
||||
personId
|
||||
@@ -54,7 +52,6 @@ describe('attachmentsResolver (e2e)', () => {
|
||||
|
||||
expect(attachments).toHaveProperty('name');
|
||||
expect(attachments).toHaveProperty('fullPath');
|
||||
expect(attachments).toHaveProperty('type');
|
||||
expect(attachments).toHaveProperty('id');
|
||||
expect(attachments).toHaveProperty('createdAt');
|
||||
expect(attachments).toHaveProperty('updatedAt');
|
||||
|
||||
+7
-5
@@ -17,7 +17,7 @@ import { createManyOperationFactory } from 'test/integration/graphql/utils/creat
|
||||
import { createOneOperationFactory } from 'test/integration/graphql/utils/create-one-operation-factory.util';
|
||||
import { destroyManyOperationFactory } from 'test/integration/graphql/utils/destroy-many-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { performCreateManyOperation } from 'test/integration/graphql/utils/perform-create-many-operation.utils';
|
||||
import { createManyOperation } from 'test/integration/graphql/utils/create-many-operation.util';
|
||||
import { updateManyOperationFactory } from 'test/integration/graphql/utils/update-many-operation-factory.util';
|
||||
import { updateOneOperationFactory } from 'test/integration/graphql/utils/update-one-operation-factory.util';
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
@@ -67,10 +67,12 @@ describe('relation connect in workspace createOne/createMany resolvers (e2e)',
|
||||
}),
|
||||
);
|
||||
|
||||
await performCreateManyOperation('company', 'companies', `id`, [
|
||||
company1,
|
||||
company2,
|
||||
]);
|
||||
await createManyOperation({
|
||||
objectMetadataSingularName: 'company',
|
||||
objectMetadataPluralName: 'companies',
|
||||
gqlFields: 'id',
|
||||
data: [company1, company2],
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
|
||||
+21
-25
@@ -20,7 +20,7 @@ import {
|
||||
TEST_PET_ID_3,
|
||||
TEST_PET_ID_4,
|
||||
} from 'test/integration/constants/test-pet-ids.constants';
|
||||
import { performCreateManyOperation } from 'test/integration/graphql/utils/perform-create-many-operation.utils';
|
||||
import { createManyOperation } from 'test/integration/graphql/utils/create-many-operation.util';
|
||||
import { search } from 'test/integration/graphql/utils/search.util';
|
||||
import { deleteAllRecords } from 'test/integration/utils/delete-all-records';
|
||||
import {
|
||||
@@ -113,6 +113,7 @@ describe('SearchResolver', () => {
|
||||
const [searchInput1Pet, searchInput2Pet, cafePet, naivePet] = pets;
|
||||
|
||||
beforeAll(async () => {
|
||||
// TODO refactor not a good practice, or should at least restore afterwards
|
||||
await deleteAllRecords('person');
|
||||
await deleteAllRecords('company');
|
||||
await deleteAllRecords('opportunity');
|
||||
@@ -124,33 +125,28 @@ describe('SearchResolver', () => {
|
||||
await deleteAllRecords('_pet');
|
||||
await deleteAllRecords('_surveyResult');
|
||||
await deleteAllRecords('_rocket');
|
||||
///
|
||||
|
||||
try {
|
||||
await performCreateManyOperation(
|
||||
'pet',
|
||||
'pets',
|
||||
OBJECT_MODEL_COMMON_FIELDS,
|
||||
pets,
|
||||
);
|
||||
await createManyOperation({
|
||||
objectMetadataSingularName: 'pet',
|
||||
objectMetadataPluralName: 'pets',
|
||||
gqlFields: OBJECT_MODEL_COMMON_FIELDS,
|
||||
data: pets,
|
||||
});
|
||||
|
||||
await performCreateManyOperation(
|
||||
'person',
|
||||
'people',
|
||||
PERSON_GQL_FIELDS,
|
||||
persons,
|
||||
);
|
||||
await createManyOperation({
|
||||
objectMetadataSingularName: 'person',
|
||||
objectMetadataPluralName: 'people',
|
||||
gqlFields: PERSON_GQL_FIELDS,
|
||||
data: persons,
|
||||
});
|
||||
|
||||
await performCreateManyOperation(
|
||||
'company',
|
||||
'companies',
|
||||
COMPANY_GQL_FIELDS,
|
||||
companies,
|
||||
);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
throw new Error('beforeAll failed');
|
||||
}
|
||||
await createManyOperation({
|
||||
objectMetadataSingularName: 'company',
|
||||
objectMetadataPluralName: 'companies',
|
||||
gqlFields: COMPANY_GQL_FIELDS,
|
||||
data: companies,
|
||||
});
|
||||
});
|
||||
|
||||
const testsUseCases: EachTestingContext<{
|
||||
|
||||
+2
-1
@@ -28,6 +28,7 @@ describe('Granular settings permissions', () => {
|
||||
originalMemberRoleId = memberRole.id;
|
||||
|
||||
// Create a custom role with canUpdateAllSettings = false
|
||||
// canUpdateAllObjectRecords must be true to allow creating records like workflows
|
||||
const createRoleQuery = {
|
||||
query: `
|
||||
mutation CreateOneRole {
|
||||
@@ -36,7 +37,7 @@ describe('Granular settings permissions', () => {
|
||||
description: "Role for testing specific setting permissions"
|
||||
canUpdateAllSettings: false
|
||||
canReadAllObjectRecords: true
|
||||
canUpdateAllObjectRecords: false
|
||||
canUpdateAllObjectRecords: true
|
||||
canSoftDeleteAllObjectRecords: false
|
||||
canDestroyAllObjectRecords: false
|
||||
}) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { type CommonResponseBody } from 'test/integration/metadata/types/common-response-body.type';
|
||||
import { warnIfErrorButNotExpectedToFail } from 'test/integration/metadata/utils/warn-if-error-but-not-expected-to-fail.util';
|
||||
import { warnIfNoErrorButExpectedToFail } from 'test/integration/metadata/utils/warn-if-no-error-but-expected-to-fail.util';
|
||||
import { type ObjectRecord } from 'twenty-shared/types';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
type CreateManyOperationInput = {
|
||||
objectMetadataSingularName: string;
|
||||
objectMetadataPluralName: string;
|
||||
gqlFields?: string;
|
||||
data: object[];
|
||||
upsert?: boolean;
|
||||
expectToFail?: boolean;
|
||||
token?: string;
|
||||
};
|
||||
|
||||
export const createManyOperation = async ({
|
||||
objectMetadataSingularName,
|
||||
objectMetadataPluralName,
|
||||
gqlFields = 'id',
|
||||
data,
|
||||
upsert = false,
|
||||
expectToFail = false,
|
||||
token,
|
||||
}: CreateManyOperationInput): CommonResponseBody<{
|
||||
createdRecords: ObjectRecord[];
|
||||
}> => {
|
||||
const graphqlOperation = createManyOperationFactory({
|
||||
objectMetadataSingularName,
|
||||
objectMetadataPluralName,
|
||||
gqlFields,
|
||||
data: data.map((item) => ({
|
||||
id: v4(),
|
||||
...item,
|
||||
})),
|
||||
upsert,
|
||||
});
|
||||
|
||||
const response = await makeGraphqlAPIRequest(graphqlOperation, token);
|
||||
|
||||
if (expectToFail === true) {
|
||||
warnIfNoErrorButExpectedToFail({
|
||||
response,
|
||||
errorMessage: 'Create many operation should have failed but did not',
|
||||
});
|
||||
}
|
||||
|
||||
if (expectToFail === false) {
|
||||
warnIfErrorButNotExpectedToFail({
|
||||
response,
|
||||
errorMessage: 'Create many operation failed but should not have',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
data: {
|
||||
createdRecords:
|
||||
response.body.data?.[`create${capitalize(objectMetadataPluralName)}`] ??
|
||||
[],
|
||||
},
|
||||
errors: response.body.errors,
|
||||
};
|
||||
};
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
import { createManyOperationFactory } from 'test/integration/graphql/utils/create-many-operation-factory.util';
|
||||
import { makeGraphqlAPIRequest } from 'test/integration/graphql/utils/make-graphql-api-request.util';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const performCreateManyOperation = async (
|
||||
objectMetadataSingularName: string,
|
||||
objectMetadataPluralName: string,
|
||||
gqlFields: string,
|
||||
data: object[],
|
||||
) => {
|
||||
const response = await makeGraphqlAPIRequest(
|
||||
createManyOperationFactory({
|
||||
objectMetadataSingularName,
|
||||
objectMetadataPluralName,
|
||||
gqlFields,
|
||||
data: data.map((item) => ({
|
||||
id: v4(),
|
||||
...item,
|
||||
})),
|
||||
}),
|
||||
);
|
||||
|
||||
return response.body.data[`create${capitalize(objectMetadataPluralName)}`];
|
||||
};
|
||||
+27
-30
@@ -190,25 +190,23 @@ describe('Standard field metadata update should succeed', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it.failing(
|
||||
'Should deactivate and reactivate standard field successfully',
|
||||
async () => {
|
||||
const deletedAtField = opportunityObjectFields.find(
|
||||
(field) => field.name === 'deletedAt',
|
||||
);
|
||||
it('Should deactivate and reactivate standard field successfully', async () => {
|
||||
const deletedAtField = opportunityObjectFields.find(
|
||||
(field) => field.name === 'deletedAt',
|
||||
);
|
||||
|
||||
jestExpectToBeDefined(deletedAtField);
|
||||
expect(deletedAtField.isActive).toBe(true);
|
||||
jestExpectToBeDefined(deletedAtField);
|
||||
expect(deletedAtField.isActive).toBe(true);
|
||||
|
||||
const { data: firstUpdateData } = await updateOneFieldMetadata({
|
||||
input: {
|
||||
idToUpdate: deletedAtField.id,
|
||||
updatePayload: {
|
||||
isActive: false,
|
||||
},
|
||||
const { data: firstUpdateData } = await updateOneFieldMetadata({
|
||||
input: {
|
||||
idToUpdate: deletedAtField.id,
|
||||
updatePayload: {
|
||||
isActive: false,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
label
|
||||
@@ -224,19 +222,19 @@ describe('Standard field metadata update should succeed', () => {
|
||||
icon
|
||||
}
|
||||
`,
|
||||
});
|
||||
});
|
||||
|
||||
expect(firstUpdateData.updateOneField.isActive).toBe(false);
|
||||
expect(firstUpdateData.updateOneField.isActive).toBe(false);
|
||||
|
||||
const { data: secondUpdateData } = await updateOneFieldMetadata({
|
||||
input: {
|
||||
idToUpdate: deletedAtField.id,
|
||||
updatePayload: {
|
||||
isActive: true,
|
||||
},
|
||||
const { data: secondUpdateData } = await updateOneFieldMetadata({
|
||||
input: {
|
||||
idToUpdate: deletedAtField.id,
|
||||
updatePayload: {
|
||||
isActive: true,
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
},
|
||||
expectToFail: false,
|
||||
gqlFields: `
|
||||
id
|
||||
name
|
||||
label
|
||||
@@ -252,11 +250,10 @@ describe('Standard field metadata update should succeed', () => {
|
||||
icon
|
||||
}
|
||||
`,
|
||||
});
|
||||
});
|
||||
|
||||
expect(secondUpdateData.updateOneField.isActive).toBe(true);
|
||||
},
|
||||
);
|
||||
expect(secondUpdateData.updateOneField.isActive).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standard field isUnique update should succeed', () => {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
const TEST_SCHEMA_NAME = 'workspace_1wgvd1injqtife6y4rvfbu3h5';
|
||||
|
||||
export const deleteAllRecords = async (objectNameSingular: string) => {
|
||||
try {
|
||||
await global.testDataSource.query(
|
||||
`DELETE from "${TEST_SCHEMA_NAME}"."${objectNameSingular}"`,
|
||||
);
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
await global.testDataSource.query(
|
||||
`DELETE from "${TEST_SCHEMA_NAME}"."${objectNameSingular}"`,
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user