Browse Source

feat(server): Add channel filtering to list query builder

Michael Bromley 7 years ago
parent
commit
7da6372f6d

+ 10 - 1
server/src/service/helpers/build-list-query.ts

@@ -1,10 +1,11 @@
-import { Type } from 'shared/shared-types';
+import { ID, Type } from 'shared/shared-types';
 import { Connection, FindManyOptions, SelectQueryBuilder } from 'typeorm';
 import { FindOptionsUtils } from 'typeorm/find-options/FindOptionsUtils';
 
 import { ListQueryOptions } from '../../common/types/common-types';
 import { VendureEntity } from '../../entity/base/base.entity';
 
+import { parseChannelParam } from './parse-channel-param';
 import { parseFilterParams } from './parse-filter-params';
 import { parseSortParams } from './parse-sort-params';
 
@@ -16,6 +17,7 @@ export function buildListQuery<T extends VendureEntity>(
     entity: Type<T>,
     options: ListQueryOptions<T> = {},
     relations?: string[],
+    channelId?: ID,
 ): SelectQueryBuilder<T> {
     const skip = options.skip;
     let take = options.take;
@@ -42,5 +44,12 @@ export function buildListQuery<T extends VendureEntity>(
         }
     });
 
+    if (channelId) {
+        const channelFilter = parseChannelParam(connection, entity, channelId);
+        if (channelFilter) {
+            qb.andWhere(channelFilter.clause, channelFilter.parameters);
+        }
+    }
+
     return qb.orderBy(sort);
 }

+ 26 - 0
server/src/service/helpers/parse-channel-param.spec.ts

@@ -0,0 +1,26 @@
+import { Channel } from '../../entity/channel/channel.entity';
+import { Customer } from '../../entity/customer/customer.entity';
+import { Product } from '../../entity/product/product.entity';
+
+import { parseChannelParam } from './parse-channel-param';
+import { MockConnection } from './parse-sort-params.spec';
+
+describe('parseChannelParam()', () => {
+    it('works with a channel-aware entity', () => {
+        const connection = new MockConnection();
+        connection.setRelations(Product, [{ propertyName: 'channels', type: Channel }]);
+        const result = parseChannelParam(connection as any, Product, 123);
+        if (!result) {
+            fail('Result should be defined');
+            return;
+        }
+        expect(result.clause).toEqual('product_channels.id = :channelId');
+        expect(result.parameters).toEqual({ channelId: 123 });
+    });
+
+    it('returns undefined for a non-channel-aware entity', () => {
+        const connection = new MockConnection();
+        const result = parseChannelParam(connection as any, Customer, 123);
+        expect(result).toBeUndefined();
+    });
+});

+ 28 - 0
server/src/service/helpers/parse-channel-param.ts

@@ -0,0 +1,28 @@
+import { ID, Type } from 'shared/shared-types';
+import { Connection } from 'typeorm';
+
+import { VendureEntity } from '../../entity/base/base.entity';
+
+import { WhereCondition } from './parse-filter-params';
+
+/**
+ * Creates a WhereCondition for a channel-aware entity, filtering for only those entities
+ * which are assigned to the channel speicified by channelId,
+ */
+export function parseChannelParam<T extends VendureEntity>(
+    connection: Connection,
+    entity: Type<T>,
+    channelId: ID,
+): WhereCondition | undefined {
+    const metadata = connection.getMetadata(entity);
+    const alias = metadata.name.toLowerCase();
+    const relations = metadata.relations;
+    const channelRelation = relations.find(r => r.propertyName === 'channels');
+    if (!channelRelation) {
+        return;
+    }
+    return {
+        clause: `${alias}_channels.id = :channelId`,
+        parameters: { channelId },
+    };
+}

+ 2 - 1
server/src/service/providers/product.service.ts

@@ -41,9 +41,10 @@ export class ProductService {
             'optionGroups',
             'variants.options',
             'variants.facetValues',
+            'channels',
         ];
 
-        return buildListQuery(this.connection, Product, options, relations)
+        return buildListQuery(this.connection, Product, options, relations, ctx.channelId)
             .getManyAndCount()
             .then(([products, totalItems]) => {
                 const items = products