Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 6 additions & 14 deletions packages/agent/src/builder/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import {
BaseDataSource,
ChartDefinition,
Collection,
DataSourceDecorator,
DataSourceFactory,
RenameCollectionCollectionDecorator,
RenameCollectionDataSourceDecorator,
TCollectionName,
TSchema,
} from '@forestadmin/datasource-toolkit';
Expand Down Expand Up @@ -70,22 +69,15 @@ export default class AgentBuilder<S extends TSchema = TSchema> {
addDataSource(factory: DataSourceFactory, options?: DataSourceOptions): this {
this.customizations.push(async () => {
const dataSource = await factory(this.options.logger);
const rename = options?.rename ?? {};
const decorated = new RenameCollectionDataSourceDecorator(dataSource);

const names = dataSource.collections.map(({ name }) => name);
const notExistName = Object.keys(rename).find(toRename => !names.includes(toRename));

if (notExistName) {
throw new Error(`The given collection name "${notExistName}" does not exist`);
for (const [oldName, newName] of Object.entries(options?.rename ?? {})) {
decorated.renameCollection(oldName, newName);
}

const decorated = new DataSourceDecorator(dataSource, RenameCollectionCollectionDecorator);
decorated.collections.forEach(collection => {
const newName = rename[collection.name];
if (newName) collection.rename(newName);

for (const collection of decorated.collections) {
this.compositeDataSource.addCollection(collection);
});
}
});

return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import { CollectionSchema, FieldSchema } from '../../interfaces/schema';
import CollectionDecorator from '../collection-decorator';
import DataSourceDecorator from '../datasource-decorator';
import CollectionRenameDataSourceDecorator from './datasource';

/**
* This decorator renames the collection name.
* This decorator renames collections.
* It should be used with RenameCollectionDataSourceDecorator, and not the raw DataSourceDecorator
*/
export default class RenameCollectionCollectionDecorator extends CollectionDecorator {
override readonly dataSource: DataSourceDecorator<RenameCollectionCollectionDecorator>;
override readonly dataSource: CollectionRenameDataSourceDecorator;

private substitutedName: string;
private substitutedName: string = null;

override get name() {
return this.substitutedName || this.childCollection.name;
return this.substitutedName ?? this.childCollection.name;
}

/** Rename the collection name */
/** @internal */
rename(name: string): void {
this.substitutedName = name;

// Invalidate all schemas
for (const collection of this.dataSource.collections) {
collection.markSchemaAsDirty();
}
}

protected override refineSchema(childSchema: CollectionSchema): CollectionSchema {
Expand All @@ -26,21 +32,21 @@ export default class RenameCollectionCollectionDecorator extends CollectionDecor
const schema = { ...oldSchema };

if (schema.type === 'ManyToOne') {
const relation = this.dataSource.getCollection(schema.foreignCollection);
schema.foreignCollection = relation.name;
schema.foreignCollection = this.getNewName(schema.foreignCollection);
} else if (schema.type === 'OneToMany' || schema.type === 'OneToOne') {
const relation = this.dataSource.getCollection(schema.foreignCollection);
schema.foreignCollection = relation.name;
schema.foreignCollection = this.getNewName(schema.foreignCollection);
} else if (schema.type === 'ManyToMany') {
const through = this.dataSource.getCollection(schema.throughCollection);
const relation = this.dataSource.getCollection(schema.foreignCollection);
schema.throughCollection = through.name;
schema.foreignCollection = relation.name;
schema.throughCollection = this.getNewName(schema.throughCollection);
schema.foreignCollection = this.getNewName(schema.foreignCollection);
}

fields[name] = schema;
}

return { ...childSchema, fields };
}

private getNewName(oldName: string): string {
return this.dataSource.collections.find(c => c.childCollection.name === oldName).name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { DataSource } from '../../interfaces/collection';
import DataSourceDecorator from '../datasource-decorator';
import RenameCollectionCollectionDecorator from './collection';

export default class RenameCollectionDataSourceDecorator extends DataSourceDecorator<RenameCollectionCollectionDecorator> {
constructor(childDataSource: DataSource) {
super(childDataSource, RenameCollectionCollectionDecorator);
}

renameCollection(oldName: string, newName: string) {
if (!this._collections[oldName]) {
throw new Error(`The given collection name "${oldName}" does not exist`);
}

if (oldName !== newName) {
const collection = this._collections[oldName] as RenameCollectionCollectionDecorator;
collection.rename(newName);

this._collections[newName] = collection;
delete this._collections[oldName];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ export default class RenameFieldCollectionDecorator extends CollectionDecorator
delete this.toChildCollection[currentName];
delete this.fromChildCollection[childName];
initialName = childName;
this.markSchemaAsDirty();
this.markAllSchemaAsDirty();
}

// Do not update arrays if renaming is a no-op (ie: customer is cancelling a previous rename).
if (initialName !== newName) {
this.fromChildCollection[initialName] = newName;
this.toChildCollection[newName] = initialName;
this.markSchemaAsDirty();
this.markAllSchemaAsDirty();
}
}

Expand Down Expand Up @@ -130,6 +130,12 @@ export default class RenameFieldCollectionDecorator extends CollectionDecorator
}));
}

private markAllSchemaAsDirty(): void {
for (const collection of this.dataSource.collections) {
collection.markSchemaAsDirty();
}
}

/** Convert field path from child collection to this collection */
private pathFromChildCollection(childPath: string): string {
if (childPath.includes(':')) {
Expand Down
2 changes: 1 addition & 1 deletion packages/datasource-toolkit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { default as CollectionCustomizationContext } from './context/collection-
// Decorators (datasource)
export { default as DataSourceDecorator } from './decorators/datasource-decorator';
export { default as ChartDataSourceDecorator } from './decorators/chart/datasource';
export { default as RenameCollectionDataSourceDecorator } from './decorators/rename-collection/datasource';

// Decorators (collections)
export { default as ActionCollectionDecorator } from './decorators/actions/collection';
Expand All @@ -22,7 +23,6 @@ export { default as OperatorsEmulateCollectionDecorator } from './decorators/ope
export { default as OperatorsReplaceCollectionDecorator } from './decorators/operators-replace/collection';
export { default as PublicationCollectionDecorator } from './decorators/publication/collection';
export { default as RenameFieldCollectionDecorator } from './decorators/rename-field/collection';
export { default as RenameCollectionCollectionDecorator } from './decorators/rename-collection/collection';
export { default as SearchCollectionDecorator } from './decorators/search/collection';
export { default as WriteCollectionDecorator } from './decorators/write/collection';
export { default as SchemaCollectionDecorator } from './decorators/schema/collection';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import * as factories from '../../__factories__';
import DataSourceDecorator from '../../../src/decorators/datasource-decorator';
import RenameCollectionCollectionDecorator from '../../../src/decorators/rename-collection/collection';
import RenameCollectionDataSourceDecorator from '../../../src/decorators/rename-collection/datasource';

describe('RenameCollectionDecorator', () => {
const setupWithOneToOneRelation = () => {
Expand Down Expand Up @@ -33,7 +32,7 @@ describe('RenameCollectionDecorator', () => {
}),
]);

return new DataSourceDecorator(dataSource, RenameCollectionCollectionDecorator);
return new RenameCollectionDataSourceDecorator(dataSource);
};

const setupWithManyToManyRelation = () => {
Expand Down Expand Up @@ -91,10 +90,16 @@ describe('RenameCollectionDecorator', () => {
}),
});

return new DataSourceDecorator(
const dataSource = new RenameCollectionDataSourceDecorator(
factories.dataSource.buildWithCollections([librariesBooks, books, libraries]),
RenameCollectionCollectionDecorator,
);

// hydrate cache to ensure invalidation is working
void dataSource.getCollection('librariesBooks').schema;
void dataSource.getCollection('books').schema;
void dataSource.getCollection('libraries').schema;

return dataSource;
};

const setupWithManyToOneAndOneToManyRelations = () => {
Expand Down Expand Up @@ -126,23 +131,23 @@ describe('RenameCollectionDecorator', () => {
}),
});

return new DataSourceDecorator(
const dataSource = new RenameCollectionDataSourceDecorator(
factories.dataSource.buildWithCollections([persons, books]),
RenameCollectionCollectionDecorator,
);

// hydrate cache to ensure invalidation is working
void dataSource.getCollection('persons').schema;
void dataSource.getCollection('books').schema;

return dataSource;
};

test('should return the real name when it is not renamed', () => {
const dataSource = factories.dataSource.buildWithCollection(
factories.collection.build({ name: 'name 1' }),
);
const decoratedDataSource = new DataSourceDecorator(
dataSource,
RenameCollectionCollectionDecorator,
);

const collection: RenameCollectionCollectionDecorator =
decoratedDataSource.getCollection('name 1');
const decoratedDataSource = new RenameCollectionDataSourceDecorator(dataSource);
const collection = decoratedDataSource.getCollection('name 1');

expect(collection.name).toEqual('name 1');
});
Expand All @@ -151,22 +156,18 @@ describe('RenameCollectionDecorator', () => {
const dataSource = factories.dataSource.buildWithCollection(
factories.collection.build({ name: 'name 1' }),
);
const decoratedDataSource = new DataSourceDecorator(
dataSource,
RenameCollectionCollectionDecorator,
);

const collection: RenameCollectionCollectionDecorator =
decoratedDataSource.getCollection('name 1');
collection.rename('name 2');
const decoratedDataSource = new RenameCollectionDataSourceDecorator(dataSource);
decoratedDataSource.renameCollection('name 1', 'name 2');

expect(collection.name).toEqual('name 2');
expect(decoratedDataSource.getCollection('name 2')).toMatchObject({ name: 'name 2' });
expect(() => decoratedDataSource.getCollection('name 1')).toThrow(
`Collection 'name 1' not found.`,
);
});

test('should change the foreign collection when it is a many to one', () => {
const dataSource = setupWithManyToOneAndOneToManyRelations();

dataSource.getCollection('persons').rename('renamedPersons');
dataSource.renameCollection('persons', 'renamedPersons');

const collection = dataSource.getCollection('books');

Expand All @@ -181,7 +182,7 @@ describe('RenameCollectionDecorator', () => {
test('should change the foreign collection when it is a one to many', () => {
const dataSource = setupWithManyToOneAndOneToManyRelations();

dataSource.getCollection('books').rename('renamedBooks');
dataSource.renameCollection('books', 'renamedBooks');

const collection = dataSource.getCollection('persons');

Expand All @@ -196,7 +197,7 @@ describe('RenameCollectionDecorator', () => {
test('should change the foreign collection when it is a one to one', () => {
const dataSource = setupWithOneToOneRelation();

dataSource.getCollection('owner').rename('renamedOwner');
dataSource.renameCollection('owner', 'renamedOwner');

const collection = dataSource.getCollection('book');

Expand All @@ -211,8 +212,8 @@ describe('RenameCollectionDecorator', () => {
test('should change the foreign collection when it is a many to many', () => {
const dataSource = setupWithManyToManyRelation();

dataSource.getCollection('librariesBooks').rename('renamedLibrariesBooks');
dataSource.getCollection('books').rename('renamedBooks');
dataSource.renameCollection('librariesBooks', 'renamedLibrariesBooks');
dataSource.renameCollection('books', 'renamedBooks');

const collection = dataSource.getCollection('libraries');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ describe('RenameFieldCollectionDecorator', () => {

// Rename stuff
beforeEach(() => {
// Cache the schemas to ensure they refresh property
void newBooks.schema;
void newBookPersons.schema;
void newPersons.schema;

// Rename stuff
newPersons.renameField('id', 'primaryKey');
newPersons.renameField('myBookPerson', 'myNovelAuthor');
newBookPersons.renameField('date', 'createdAt');
Expand Down Expand Up @@ -316,6 +322,12 @@ describe('RenameFieldCollectionDecorator', () => {

describe('when renaming foreign keys', () => {
beforeEach(() => {
// Cache the schemas to ensure they refresh property
void newBooks.schema;
void newBookPersons.schema;
void newPersons.schema;

// Rename stuff
newBookPersons.renameField('bookId', 'novelId');
newBookPersons.renameField('personId', 'authorId');
});
Expand Down