Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: error
Browse files Browse the repository at this point in the history
alban bertolini committed Oct 2, 2024
1 parent 5484cec commit ba86837
Showing 21 changed files with 129 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -39,11 +39,11 @@ export default class OperatorsEmulateCollectionDecorator extends CollectionDecor
// Check that the collection can actually support our rewriting
const pks = SchemaUtils.getPrimaryKeys(this.childCollection.schema);
pks.forEach(pk => {
const schema = SchemaUtils.getColumn(
const schema = SchemaUtils.getField(
this.childCollection.schema,
pk,
this.childCollection.name,
);
) as ColumnSchema;
const operators = schema.filterOperators;

if (!operators?.has('Equal') || !operators?.has('In')) {
@@ -55,13 +55,8 @@ export default class OperatorsEmulateCollectionDecorator extends CollectionDecor
});

// Check that targeted field is valid
const field = SchemaUtils.getColumn(
this.childCollection.schema,
name,
this.childCollection.name,
);
SchemaUtils.throwIfMissingField(this.childCollection.schema, name, this.childCollection.name);
FieldValidator.validate(this, name);
if (!field) throw new Error('Cannot replace operator for relation');

// Mark the field operator as replaced.
if (!this.fields.has(name)) this.fields.set(name, new Map());
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */

import { ColumnSchema, ConditionTreeLeaf, Sort } from '@forestadmin/datasource-toolkit';
import {
ColumnSchema,
ConditionTreeLeaf,
MissingFieldError,
Sort,
} from '@forestadmin/datasource-toolkit';
import * as factories from '@forestadmin/datasource-toolkit/dist/test/__factories__';

import {
@@ -322,9 +327,7 @@ describe('Builder > Collection', () => {
const { dsc, customizer } = await setup();

customizer.importField('translatorName', { path: 'doesNotExistPath' });
await expect(dsc.getDataSource(logger)).rejects.toThrow(
new MissingFieldError('doesNotExistPath', 'authors'),
);
await expect(dsc.getDataSource(logger)).rejects.toThrow(MissingFieldError);
});
});
});
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import {
Collection,
DataSource,
DataSourceDecorator,
MissingFieldError,
PaginatedFilter,
Projection,
} from '@forestadmin/datasource-toolkit';
@@ -102,7 +103,7 @@ describe('ComputedDecorator', () => {
dependencies: ['__nonExisting__'],
getValues: () => Promise.reject(),
});
}).toThrow(new MissingFieldError('__nonExisting__', 'books'));
}).toThrow(MissingFieldError);
});

test('should throw if defining a field with invalid dependencies', () => {
Original file line number Diff line number Diff line change
@@ -4,9 +4,11 @@ import {
ConditionTreeLeaf,
DataSource,
DataSourceDecorator,
MissingFieldError,
PaginatedFilter,
Projection,
RecordData,
RelationFieldAccessDeniedError,
} from '@forestadmin/datasource-toolkit';
import * as factories from '@forestadmin/datasource-toolkit/dist/test/__factories__';

@@ -115,7 +117,7 @@ describe('OperatorsEmulateCollectionDecorator', () => {

test('emulateFieldOperator() should throw if the field does not exists', () => {
expect(() => newBooks.emulateFieldOperator('__dontExist', 'Equal')).toThrow(
new MissingFieldError('__dontExist', 'books'),
MissingFieldError,
);
});

@@ -127,7 +129,7 @@ describe('OperatorsEmulateCollectionDecorator', () => {

test('emulateFieldOperator() should throw if the field is in a relation', () => {
expect(() => newBooks.emulateFieldOperator('author:firstName', 'Equal')).toThrow(
'Cannot replace operator for relation',
RelationFieldAccessDeniedError,
);
});

Original file line number Diff line number Diff line change
@@ -85,9 +85,7 @@ describe('PublicationCollectionDecorator', () => {
});

test('should throw when hiding a field which does not exists', () => {
expect(() => newPersons.changeFieldVisibility('unknown', false)).toThrow(
new MissingFieldError('unknown', 'persons'),
);
expect(() => newPersons.changeFieldVisibility('unknown', false)).toThrow(MissingFieldError);
});

test('should throw when hiding the primary key', () => {
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@ import {
DataSourceDecorator,
Filter,
ManyToManySchema,
MissingColumnError,
MissingFieldError,
PaginatedFilter,
Projection,
Sort,
@@ -165,7 +167,7 @@ describe('RelationCollectionDecorator', () => {
foreignCollection: 'passports',
originKey: '__nonExisting__',
}),
).toThrow(new MissingFieldError('__nonExisting__', 'passports'));
).toThrow(MissingColumnError);
});
});

@@ -282,7 +284,7 @@ describe('RelationCollectionDecorator', () => {
foreignCollection: 'persons',
foreignKey: '__nonExisting__',
}),
).toThrow(new MissingFieldError('__nonExisting__', 'passports'));
).toThrow(MissingFieldError);
});
});

@@ -350,7 +352,7 @@ describe('RelationCollectionDecorator', () => {
originKey: '__nonExisting__',
throughCollection: 'passports',
} as ManyToManySchema),
).toThrow(new MissingFieldError('__nonExisting__', 'passports'));
).toThrow(MissingFieldError);
});

test('should throw with a non existent fk', () => {
@@ -362,7 +364,7 @@ describe('RelationCollectionDecorator', () => {
originKey: 'ownerId',
throughCollection: 'passports',
} as ManyToManySchema),
).toThrow(new MissingFieldError('__nonExisting__', 'passports'));
).toThrow(MissingFieldError);
});
});

Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import {
DataSource,
DataSourceDecorator,
Filter,
MissingFieldError,
PaginatedFilter,
Projection,
Sort,
@@ -130,14 +131,12 @@ describe('RenameFieldCollectionDecorator', () => {
});

test('should throw when renaming a field which does not exists', () => {
expect(() => newPersons.renameField('unknown', 'somethingnew')).toThrow(
new MissingFieldError('unknown'),
);
expect(() => newPersons.renameField('unknown', 'somethingnew')).toThrow(MissingFieldError);
});

test('should throw when renaming a field using an older name', () => {
newPersons.renameField('id', 'key');
expect(() => newPersons.renameField('id', 'primaryKey')).toThrow(new MissingFieldError('id'));
expect(() => newPersons.renameField('id', 'primaryKey')).toThrow(MissingFieldError);
});

test('should throw when renaming with a name including space', () => {
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Collection, DataSource } from '@forestadmin/datasource-toolkit';
import { Collection, DataSource, MissingFieldError } from '@forestadmin/datasource-toolkit';
import * as factories from '@forestadmin/datasource-toolkit/dist/test/__factories__';

import SegmentCollectionDecorator from '../../../src/decorators/segment/collection';
@@ -102,9 +102,7 @@ describe('SegmentCollectionDecorator', () => {
factories.caller.build(),
factories.filter.build({ segment: 'segmentName' }),
),
).rejects.toThrow(
"The 'books.do not exists' field was not found. Available fields are: [name]. Please check if the field name is correct.",
);
).rejects.toThrow(MissingFieldError);

expect(conditionTreeGenerator).toHaveBeenCalled();
});
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ import {
ColumnSchema,
DataSource,
DataSourceDecorator,
MissingFieldError,
Page,
PaginatedFilter,
Projection,
@@ -93,9 +94,7 @@ describe('SortEmulationDecoratorCollection', () => {
});

test('emulateFieldSorting() should throw if the field does not exists', () => {
expect(() => newBooks.emulateFieldSorting('__dontExist')).toThrow(
new MissingFieldError('__dontExist', 'books'),
);
expect(() => newBooks.emulateFieldSorting('__dontExist')).toThrow(MissingFieldError);
});

test('emulateFieldSorting() should throw if the field is a relation', () => {
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Collection, DataSource, DataSourceDecorator } from '@forestadmin/datasource-toolkit';
import {
Collection,
DataSource,
DataSourceDecorator,
MissingFieldError,
RelationFieldAccessDeniedError,
} from '@forestadmin/datasource-toolkit';
import * as factories from '@forestadmin/datasource-toolkit/dist/test/__factories__';

import ValidationDecorator from '../../../src/decorators/validation/collection';
@@ -49,7 +55,7 @@ describe('SortEmulationDecoratorCollection', () => {

test('addValidation() should throw if the field does not exists', () => {
expect(() => newBooks.addValidation('__dontExist', { operator: 'Present' })).toThrow(
new MissingFieldError('__dontExist', 'books'),
MissingFieldError,
);
});

@@ -67,7 +73,7 @@ describe('SortEmulationDecoratorCollection', () => {

test('addValidation() should throw if the field is in a relation', () => {
expect(() => newBooks.addValidation('author:firstName', { operator: 'Present' })).toThrow(
'Cannot add validators on a relation, use the foreign key instead',
RelationFieldAccessDeniedError,
);
});

Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ describe('WriteDecorator > When their are no relations', () => {
const decorator = new WriteDecorator(collection, dataSource);

expect(() => decorator.replaceFieldWriting('inexistant', () => ({}))).toThrow(
new MissingFieldError('inexistant', 'books'),
MissingFieldError,
);
});

Original file line number Diff line number Diff line change
@@ -53,6 +53,6 @@ describe('importField', () => {
path: 'INVALID',
name: 'NOPE',
}),
).rejects.toThrow(new MissingFieldError('INVALID', 'collection1'));
).rejects.toThrow(MissingFieldError);
});
});
57 changes: 46 additions & 11 deletions packages/datasource-toolkit/src/errors.ts
Original file line number Diff line number Diff line change
@@ -77,26 +77,61 @@ export class MissingSchemaElementError extends ValidationError {}

export class MissingCollectionError extends MissingSchemaElementError {}

export class MissingFieldError extends MissingSchemaElementError {
constructor(options: {
typeOfField: 'Field' | 'Relation' | 'Column';
fieldName: string;
availableFields: string[];
collectionName?: string;
}) {
const { typeOfField, fieldName, availableFields, collectionName } = options;
const path = collectionName ? `${collectionName}.${fieldName}` : fieldName;
function buildPath(fieldName: string, collectionName?: string): string {
return collectionName ? `${collectionName}.${fieldName}` : fieldName;
}

type MissingFieldErrorOptions = {
fieldName: string;
availableFields: string[];
collectionName?: string;
};

function buildMessageMissingElement(options: {
typeOfField: 'Field' | 'Column' | 'Relation';
fieldName: string;
availableFields: string[];
collectionName?: string;
}): string {
const { typeOfField, fieldName, availableFields, collectionName } = options;
const path = buildPath(fieldName, collectionName);

return `The '${path}' ${typeOfField.toLowerCase()} was not found. Available ${typeOfField.toLowerCase()}s are: [${availableFields}]. Please check if the ${typeOfField.toLowerCase()} name is correct.`;
}

export class RelationFieldAccessDeniedError extends ValidationError {
constructor(options: Pick<MissingFieldErrorOptions, 'fieldName' | 'collectionName'>) {
const { fieldName, collectionName } = options;
const path = buildPath(fieldName, collectionName);

super(
`The '${path}' ${typeOfField.toLowerCase()} was not found. Available ${typeOfField.toLowerCase()}s are: [${availableFields}]. Please check if the ${typeOfField.toLowerCase()} name is correct.`,
`Access to the '${path}' field is denied. You are trying to access a field from a related entity, but this is not allowed in the current context. Please verify the field name and context of use.`,
);
}
}

export class MissingFieldError extends MissingSchemaElementError {
constructor(options: MissingFieldErrorOptions) {
super(buildMessageMissingElement({ typeOfField: 'Field', ...options }));
}
}

export class MissingColumnError extends MissingSchemaElementError {
constructor(options: MissingFieldErrorOptions) {
super(buildMessageMissingElement({ typeOfField: 'Column', ...options }));
}
}

export class MissingRelationError extends MissingSchemaElementError {
constructor(options: MissingFieldErrorOptions) {
super(buildMessageMissingElement({ typeOfField: 'Relation', ...options }));
}
}

export class AlreadyDefinedFieldError extends ValidationError {
constructor(options: { fieldName: string; collectionName?: string }) {
const { fieldName, collectionName } = options;
const path = collectionName ? `${collectionName}.${fieldName}` : fieldName;
const path = buildPath(fieldName, collectionName);

super(
`The '${path}' field is already defined. Please check if the field name is correct and unique.`,
Loading

0 comments on commit ba86837

Please sign in to comment.