[Flexible-schema] Add findOne and fix findMany pagination + soft-delete for graphql-query-runner (#6978)

This commit is contained in:
Weiko
2024-09-11 11:29:56 +02:00
committed by GitHub
parent 425eb040f7
commit 1317e1c4f2
11 changed files with 355 additions and 109 deletions
@@ -26,15 +26,19 @@ export class GraphqlQueryFilterConditionParser {
}
const result: FindOptionsWhere<ObjectLiteral> = {};
let orCondition: FindOptionsWhere<ObjectLiteral>[] | null = null;
for (const [key, value] of Object.entries(conditions)) {
switch (key) {
case 'and':
return this.parseAndCondition(value, isNegated);
Object.assign(result, this.parseAndCondition(value, isNegated));
break;
case 'or':
return this.parseOrCondition(value, isNegated);
orCondition = this.parseOrCondition(value, isNegated);
break;
case 'not':
return this.parse(value, !isNegated);
Object.assign(result, this.parse(value, !isNegated));
break;
default:
Object.assign(
result,
@@ -43,6 +47,10 @@ export class GraphqlQueryFilterConditionParser {
}
}
if (orCondition) {
return orCondition.map((condition) => ({ ...result, ...condition }));
}
return result;
}
@@ -28,7 +28,15 @@ export class GraphqlQueryFilterOperatorParser {
lt: (value: any) => LessThan(value),
lte: (value: any) => LessThanOrEqual(value),
in: (value: any) => In(value),
is: (value: any) => (value === 'NULL' ? IsNull() : value),
is: (value: any) => {
if (value === 'NULL') {
return IsNull();
} else if (value === 'NOT_NULL') {
return Not(IsNull());
} else {
return value;
}
},
like: (value: string) => Like(`%${value}%`),
ilike: (value: string) => ILike(`%${value}%`),
startsWith: (value: string) => ILike(`${value}%`),
@@ -23,7 +23,10 @@ export class GraphqlQueryOrderFieldParser {
this.fieldMetadataMap = fieldMetadataMap;
}
parse(orderBy: RecordOrderBy): Record<string, FindOptionsOrderValue> {
parse(
orderBy: RecordOrderBy,
isForwardPagination = true,
): Record<string, FindOptionsOrderValue> {
return orderBy.reduce(
(acc, item) => {
Object.entries(item).forEach(([key, value]) => {
@@ -40,11 +43,15 @@ export class GraphqlQueryOrderFieldParser {
const compositeOrder = this.parseCompositeFieldForOrder(
fieldMetadata,
value,
isForwardPagination,
);
Object.assign(acc, compositeOrder);
} else {
acc[key] = this.convertOrderByToFindOptionsOrder(value);
acc[key] = this.convertOrderByToFindOptionsOrder(
value,
isForwardPagination,
);
}
});
@@ -57,6 +64,7 @@ export class GraphqlQueryOrderFieldParser {
private parseCompositeFieldForOrder(
fieldMetadata: FieldMetadataInterface,
value: any,
isForwardPagination = true,
): Record<string, FindOptionsOrderValue> {
const compositeType = compositeTypeDefinitions.get(
fieldMetadata.type as CompositeFieldMetadataType,
@@ -87,8 +95,10 @@ export class GraphqlQueryOrderFieldParser {
`Sub field order by value must be of type OrderByDirection, but got: ${subFieldValue}`,
);
}
acc[fullFieldName] =
this.convertOrderByToFindOptionsOrder(subFieldValue);
acc[fullFieldName] = this.convertOrderByToFindOptionsOrder(
subFieldValue,
isForwardPagination,
);
return acc;
},
@@ -98,16 +108,29 @@ export class GraphqlQueryOrderFieldParser {
private convertOrderByToFindOptionsOrder(
direction: OrderByDirection,
isForwardPagination = true,
): FindOptionsOrderValue {
switch (direction) {
case OrderByDirection.AscNullsFirst:
return { direction: 'ASC', nulls: 'FIRST' };
return {
direction: isForwardPagination ? 'ASC' : 'DESC',
nulls: 'FIRST',
};
case OrderByDirection.AscNullsLast:
return { direction: 'ASC', nulls: 'LAST' };
return {
direction: isForwardPagination ? 'ASC' : 'DESC',
nulls: 'LAST',
};
case OrderByDirection.DescNullsFirst:
return { direction: 'DESC', nulls: 'FIRST' };
return {
direction: isForwardPagination ? 'DESC' : 'ASC',
nulls: 'FIRST',
};
case OrderByDirection.DescNullsLast:
return { direction: 'DESC', nulls: 'LAST' };
return {
direction: isForwardPagination ? 'DESC' : 'ASC',
nulls: 'LAST',
};
default:
throw new GraphqlQueryRunnerException(
`Invalid direction: ${direction}`,
@@ -1,6 +1,7 @@
import {
FindOptionsOrderValue,
FindOptionsWhere,
IsNull,
ObjectLiteral,
} from 'typeorm';
@@ -32,20 +33,55 @@ export class GraphqlQueryParser {
parseFilter(
recordFilter: RecordFilter,
shouldAddDefaultSoftDeleteCondition = false,
): FindOptionsWhere<ObjectLiteral> | FindOptionsWhere<ObjectLiteral>[] {
const graphqlQueryFilterParser = new GraphqlQueryFilterParser(
this.fieldMetadataMap,
);
return graphqlQueryFilterParser.parse(recordFilter);
const parsedFilter = graphqlQueryFilterParser.parse(recordFilter);
if (
!shouldAddDefaultSoftDeleteCondition ||
!('deletedAt' in this.fieldMetadataMap)
) {
return parsedFilter;
}
return this.addDefaultSoftDeleteCondition(parsedFilter);
}
parseOrder(orderBy: RecordOrderBy): Record<string, FindOptionsOrderValue> {
private addDefaultSoftDeleteCondition(
filter: FindOptionsWhere<ObjectLiteral> | FindOptionsWhere<ObjectLiteral>[],
): FindOptionsWhere<ObjectLiteral> | FindOptionsWhere<ObjectLiteral>[] {
if (Array.isArray(filter)) {
return filter.map((condition) =>
this.addSoftDeleteToCondition(condition),
);
}
return this.addSoftDeleteToCondition(filter);
}
private addSoftDeleteToCondition(
condition: FindOptionsWhere<ObjectLiteral>,
): FindOptionsWhere<ObjectLiteral> {
if (!('deletedAt' in condition)) {
return { ...condition, deletedAt: IsNull() };
}
return condition;
}
parseOrder(
orderBy: RecordOrderBy,
isForwardPagination = true,
): Record<string, FindOptionsOrderValue> {
const graphqlQueryOrderParser = new GraphqlQueryOrderParser(
this.fieldMetadataMap,
);
return graphqlQueryOrderParser.parse(orderBy);
return graphqlQueryOrderParser.parse(orderBy, isForwardPagination);
}
parseSelectedFields(