|
|
@@ -19,6 +19,7 @@ import { ChannelAware, SoftDeletable } from '../common/types/common-types';
|
|
|
import { Logger } from '../config/index';
|
|
|
import { VendureEntity } from '../entity/base/base.entity';
|
|
|
|
|
|
+import { removeCustomFieldsWithEagerRelations } from './remove-custom-fields-with-eager-relations';
|
|
|
import { TransactionWrapper } from './transaction-wrapper';
|
|
|
import { GetEntityOrThrowOptions } from './types';
|
|
|
|
|
|
@@ -263,10 +264,31 @@ export class TransactionalConnection {
|
|
|
channelId: ID,
|
|
|
options: FindOneOptions = {},
|
|
|
) {
|
|
|
- const qb = this.getRepository(ctx, entity).createQueryBuilder('entity');
|
|
|
- options.relations = this.removeCustomFieldsWithEagerRelations(qb, options.relations);
|
|
|
- FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, options);
|
|
|
- if (options.loadEagerRelations !== false) {
|
|
|
+ let qb = this.getRepository(ctx, entity).createQueryBuilder('entity');
|
|
|
+ options.relations = removeCustomFieldsWithEagerRelations(qb, options.relations);
|
|
|
+ let skipEagerRelations = false;
|
|
|
+ try {
|
|
|
+ FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, options);
|
|
|
+ } catch (e: any) {
|
|
|
+ // https://github.com/vendure-ecommerce/vendure/issues/1664
|
|
|
+ // This is a failsafe to catch edge cases related to the TypeORM
|
|
|
+ // bug described in the doc block of `removeCustomFieldsWithEagerRelations`.
|
|
|
+ // In this case, a nested custom field relation has an eager-loaded relation,
|
|
|
+ // and is throwing an error. In this case we throw our hands up and say
|
|
|
+ // "sod it!", refuse to load _any_ relations at all, and rely on the
|
|
|
+ // GraphQL entity resolvers to take care of them.
|
|
|
+ Logger.debug(
|
|
|
+ `TransactionalConnection.findOneInChannel ran into issues joining nested custom field relations. Running the query without joining any relations instead.`,
|
|
|
+ );
|
|
|
+ qb = this.getRepository(ctx, entity).createQueryBuilder('entity');
|
|
|
+ FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, {
|
|
|
+ ...options,
|
|
|
+ relations: [],
|
|
|
+ loadEagerRelations: false,
|
|
|
+ });
|
|
|
+ skipEagerRelations = true;
|
|
|
+ }
|
|
|
+ if (options.loadEagerRelations !== false && !skipEagerRelations) {
|
|
|
// tslint:disable-next-line:no-non-null-assertion
|
|
|
FindOptionsUtils.joinEagerRelations(qb, qb.alias, qb.expressionMap.mainAlias!.metadata);
|
|
|
}
|
|
|
@@ -277,65 +299,6 @@ export class TransactionalConnection {
|
|
|
.getOne();
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * This is a work-around for this issue: https://github.com/vendure-ecommerce/vendure/issues/1664
|
|
|
- *
|
|
|
- * Explanation:
|
|
|
- * When calling `FindOptionsUtils.joinEagerRelations()`, there appears to be a bug in TypeORM whereby
|
|
|
- * it will throw the following error *if* the `options.relations` array contains any customField relations
|
|
|
- * where the related entity itself has eagerly-loaded relations.
|
|
|
- *
|
|
|
- * For example, let's say we define a custom field on the Product entity like this:
|
|
|
- * ```
|
|
|
- * Product: [{
|
|
|
- * name: 'featuredFacet',
|
|
|
- * type: 'relation',
|
|
|
- * entity: Facet,
|
|
|
- * }],
|
|
|
- * ```
|
|
|
- * and then we pass into `TransactionalConnection.findOneInChannel()` an options array of:
|
|
|
- *
|
|
|
- * ```
|
|
|
- * { relations: ['customFields.featuredFacet'] }
|
|
|
- * ```
|
|
|
- * it will throw an error because the `Facet` entity itself has eager relations (namely the `translations` property).
|
|
|
- * This will cause TypeORM to throw the error:
|
|
|
- * ```
|
|
|
- * TypeORMError: "entity__customFields" alias was not found. Maybe you forgot to join it?
|
|
|
- * ```
|
|
|
- *
|
|
|
- * So this method introspects the QueryBuilder metadata and checks for any custom field relations which
|
|
|
- * themselves have eager relations. If found, it removes those items from the `options.relations` array.
|
|
|
- *
|
|
|
- * TODO: Ideally create a minimal reproduction case and report in the TypeORM repo for an upstream fix.
|
|
|
- */
|
|
|
- private removeCustomFieldsWithEagerRelations(
|
|
|
- qb: SelectQueryBuilder<any>,
|
|
|
- relations: string[] = [],
|
|
|
- ): string[] {
|
|
|
- let resultingRelations = relations;
|
|
|
- const mainAlias = qb.expressionMap.mainAlias;
|
|
|
- const customFieldsMetadata = mainAlias?.metadata.embeddeds.find(
|
|
|
- metadata => metadata.propertyName === 'customFields',
|
|
|
- );
|
|
|
- if (customFieldsMetadata) {
|
|
|
- const customFieldRelationsWithEagerRelations = customFieldsMetadata.relations.filter(
|
|
|
- relation => !!relation.inverseEntityMetadata.ownRelations.find(or => or.isEager === true),
|
|
|
- );
|
|
|
- for (const relation of customFieldRelationsWithEagerRelations) {
|
|
|
- const propertyName = relation.propertyName;
|
|
|
- const relationsToRemove = relations.filter(r => r.startsWith(`customFields.${propertyName}`));
|
|
|
- if (relationsToRemove.length) {
|
|
|
- Logger.debug(
|
|
|
- `TransactionalConnection.findOneInChannel cannot automatically join relation [${mainAlias?.metadata.name}.customFields.${propertyName}]`,
|
|
|
- );
|
|
|
- resultingRelations = relations.filter(r => !r.startsWith(`customFields.${propertyName}`));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return resultingRelations;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* @description
|
|
|
* Like the TypeOrm `Repository.findByIds()` method, but limits the results to
|