Fixed NQL filter column mapping for BookskhelfRepository

no issue

When filtering and using the BookshelfRepository, you had to use the column names instead of the entitiy field names. This is fixed by adding a transformer. Long term we want to move the NQL parsing away from the repository and into the API layer, and pass along the Mongo filter probably.
This commit is contained in:
Simon Backx 2023-09-14 14:28:09 +02:00 committed by Simon Backx
parent b1cd7dd898
commit 383069a1e5
3 changed files with 29 additions and 8 deletions

View File

@ -25,6 +25,7 @@
"sinon": "15.2.0"
},
"dependencies": {
"@tryghost/nql": "0.11.0"
"@tryghost/mongo-utils": "0.5.0",
"knex": "2.4.2"
}
}

View File

@ -1,8 +1,9 @@
import {Knex} from 'knex';
import {mapKeys, chainTransformers} from '@tryghost/mongo-utils';
type Entity<T> = {
id: T;
deleted: boolean;
deleted: boolean
}
type Order<T> = {
@ -14,7 +15,7 @@ export type ModelClass<T> = {
destroy: (data: {id: T}) => Promise<void>;
findOne: (data: {id: T}, options?: {require?: boolean}) => Promise<ModelInstance<T> | null>;
add: (data: object) => Promise<ModelInstance<T>>;
getFilteredCollection: (options: {filter?: string}) => {
getFilteredCollection: (options: {filter?: string, mongoTransformer?: unknown}) => {
count(): Promise<number>,
query: (f: (q: Knex.QueryBuilder) => void) => void,
fetchAll: () => Promise<ModelInstance<T>[]>
@ -39,14 +40,26 @@ export abstract class BookshelfRepository<IDType, T extends Entity<IDType>> {
}
protected abstract toPrimitive(entity: T): object;
protected abstract entityFieldToColumn(field: keyof T): string;
protected abstract modelToEntity(model: ModelInstance<IDType>): Promise<T|null> | T | null;
protected abstract modelToEntity (model: ModelInstance<IDType>): Promise<T|null> | T | null
protected abstract getFieldToColumnMap(): Record<keyof T, string>;
#entityFieldToColumn(field: keyof T): string {
const mapping = this.getFieldToColumnMap();
return mapping[field];
}
#orderToString(order?: OrderOption<T>) {
if (!order || order.length === 0) {
return;
}
return order.map(({field, direction}) => `${this.entityFieldToColumn(field)} ${direction}`).join(',');
return order.map(({field, direction}) => `${this.#entityFieldToColumn(field)} ${direction}`).join(',');
}
/**
* Map all the fields in an NQL filter to the names of the model
*/
#getNQLKeyTransformer() {
return chainTransformers(...mapKeys(this.getFieldToColumnMap()));
}
async save(entity: T): Promise<void> {
@ -70,7 +83,10 @@ export abstract class BookshelfRepository<IDType, T extends Entity<IDType>> {
}
async #fetchAll({filter, order, page, limit}: { filter?: string; order?: OrderOption<T>; page?: number; limit?: number }): Promise<T[]> {
const collection = this.Model.getFilteredCollection({filter});
const collection = this.Model.getFilteredCollection({
filter,
mongoTransformer: this.#getNQLKeyTransformer()
});
const orderString = this.#orderToString(order);
if ((limit && page) || orderString) {
@ -110,7 +126,10 @@ export abstract class BookshelfRepository<IDType, T extends Entity<IDType>> {
}
async getCount({filter}: { filter?: string } = {}): Promise<number> {
const collection = this.Model.getFilteredCollection({filter});
const collection = this.Model.getFilteredCollection({
filter,
mongoTransformer: this.#getNQLKeyTransformer()
});
return await collection.count();
}
}

View File

@ -0,0 +1 @@
declare module '@tryghost/mongo-utils';