[permissions] Adapt field permissions to connect + createMany (#13655)
In this PR we adapt field permission to two things 1. Connect: the recent insertion of the connect feature introduces the possibility to have "connect" objects in typeORM's expressionMap's valueSet, where we previously only had column names (if i followed correctly). For instance for a person object that a N - 1 relationship to company, person will have both companyId and company as possible valueSet keys for the upsert. We need to reflect that in our `getColumnNameToFieldMetadataIdMap` util that returns a map containing every possible value we could encounter in valueSet. In an attempt to tie this to the schema generation where this is introduced, I created the shallow util extractGraphQLRelationFieldNames (probably ill-named - im willing to update the name if @etiennejouan has a better idea?) to remind us that these two are linked. 2. CreateMany: When calling query builders methods directly on custom object (like we do in graphql-create-many-resolver), we need to be careful to call them with a selection of readable fields. We had a debate on whether we should compute this selection containing all readable fields by default under the hood when no selected fields are indicated. I am still not 100% convinced as I think it should remain the caller's responsibility, but this case reminds us that it could easily be forgotten by developers - although it is all the more the case as we don't have seeds yet that help us realize that ([PR on the way](https://github.com/twentyhq/twenty/pull/13646)).
This commit is contained in:
@@ -40,7 +40,7 @@ export class WorkspaceRepository<
|
||||
private readonly internalContext: WorkspaceInternalContext;
|
||||
private shouldBypassPermissionChecks: boolean;
|
||||
private featureFlagMap: FeatureFlagMap;
|
||||
private objectRecordsPermissions?: ObjectRecordsPermissions;
|
||||
public readonly objectRecordsPermissions?: ObjectRecordsPermissions;
|
||||
private authContext?: AuthContext;
|
||||
declare manager: WorkspaceEntityManager;
|
||||
|
||||
|
||||
+2
-1
@@ -226,8 +226,9 @@ describe('getColumnNameToFieldMetadataIdMap', () => {
|
||||
);
|
||||
|
||||
expect(result['companyId']).toBe('field-1');
|
||||
expect(result['company']).toBe('field-1');
|
||||
expect(result['name']).toBe('field-2');
|
||||
expect(Object.keys(result)).toHaveLength(2);
|
||||
expect(Object.keys(result)).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('should skip ONE_TO_MANY relation field types', () => {
|
||||
|
||||
+10
-3
@@ -1,3 +1,5 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface';
|
||||
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
@@ -34,12 +36,17 @@ export function getColumnNameToFieldMetadataIdMap(
|
||||
},
|
||||
processRelationField: ({
|
||||
fieldMetadataId,
|
||||
columnName,
|
||||
joinColumnName,
|
||||
connectFieldName,
|
||||
}: {
|
||||
fieldMetadataId: string;
|
||||
columnName: string;
|
||||
joinColumnName: string;
|
||||
connectFieldName?: string;
|
||||
}) => {
|
||||
columnNameToFieldMetadataIdMap[columnName] = fieldMetadataId;
|
||||
columnNameToFieldMetadataIdMap[joinColumnName] = fieldMetadataId;
|
||||
if (isDefined(connectFieldName)) {
|
||||
columnNameToFieldMetadataIdMap[connectFieldName] = fieldMetadataId;
|
||||
}
|
||||
},
|
||||
processSimpleField: ({
|
||||
fieldMetadataId,
|
||||
|
||||
+3
-4
@@ -40,13 +40,12 @@ export function getFieldMetadataIdToColumnNamesMap(
|
||||
},
|
||||
processRelationField: ({
|
||||
fieldMetadataId,
|
||||
columnName,
|
||||
joinColumnName,
|
||||
}: {
|
||||
fieldMetadataId: string;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
columnName: string;
|
||||
joinColumnName: string;
|
||||
}) => {
|
||||
fieldMetadataToColumnNamesMap.set(fieldMetadataId, [columnName]); // TODO test
|
||||
fieldMetadataToColumnNamesMap.set(fieldMetadataId, [joinColumnName]);
|
||||
},
|
||||
processSimpleField: ({
|
||||
fieldMetadataId,
|
||||
|
||||
+10
-10
@@ -2,6 +2,7 @@ import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interf
|
||||
import { FieldMetadataRelationSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||
import { RelationType } from 'src/engine/metadata-modules/field-metadata/interfaces/relation-type.interface';
|
||||
|
||||
import { extractGraphQLRelationFieldNames } from 'src/engine/api/graphql/workspace-schema-builder/utils/extract-graphql-relation-field-names.util';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { compositeTypeDefinitions } from 'src/engine/metadata-modules/field-metadata/composite-types';
|
||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||
@@ -26,11 +27,13 @@ export type ColumnNameProcessor = {
|
||||
processRelationField: ({
|
||||
fieldMetadataId,
|
||||
fieldMetadata,
|
||||
columnName,
|
||||
joinColumnName,
|
||||
connectFieldName,
|
||||
}: {
|
||||
fieldMetadataId: string;
|
||||
fieldMetadata: FieldMetadataEntity;
|
||||
columnName: string;
|
||||
joinColumnName: string;
|
||||
connectFieldName?: string;
|
||||
}) => void;
|
||||
processSimpleField: ({
|
||||
fieldMetadataId,
|
||||
@@ -73,18 +76,15 @@ export function processFieldMetadataForColumnNameMapping(
|
||||
if (fieldMetadataSettings?.relationType === RelationType.ONE_TO_MANY) {
|
||||
continue;
|
||||
}
|
||||
const columnName = fieldMetadataSettings?.joinColumnName;
|
||||
|
||||
if (!columnName) {
|
||||
throw new PermissionsException(
|
||||
`Join column name is required for relation field metadata ${fieldMetadata.name}`,
|
||||
PermissionsExceptionCode.JOIN_COLUMN_NAME_REQUIRED,
|
||||
);
|
||||
}
|
||||
const { joinColumnName, fieldMetadataName } =
|
||||
extractGraphQLRelationFieldNames(fieldMetadata);
|
||||
|
||||
processor.processRelationField({
|
||||
fieldMetadataId,
|
||||
fieldMetadata,
|
||||
columnName,
|
||||
joinColumnName,
|
||||
connectFieldName: fieldMetadataName,
|
||||
});
|
||||
} else {
|
||||
const columnName = computeColumnName(fieldMetadata);
|
||||
|
||||
Reference in New Issue
Block a user