Browse Source

feat(server): Implement pagination for customers & products queries

Michael Bromley 7 years ago
parent
commit
b90f3aabdc

+ 6 - 1
server/src/api/customer/customer.api.graphql

@@ -1,5 +1,5 @@
 type Query {
-  customers: [Customer]
+  customers(take: Int, skip: Int): CustomerList
   customer(id: Int!): Customer
 }
 
@@ -9,3 +9,8 @@ type Mutation {
   "Create a new Address and associate it with the Customer specified by customerId"
   createCustomerAddress(customerId: Int, input: CreateAddressInput): Address
 }
+
+type CustomerList implements PaginatedList {
+    items: [Customer!]!
+    totalItems: Int!
+}

+ 2 - 1
server/src/api/customer/customer.controller.ts

@@ -1,4 +1,5 @@
 import { Controller, Get, Param } from '@nestjs/common';
+import { PaginatedList } from '../../common/common-types';
 import { Customer } from '../../entity/customer/customer.entity';
 import { CustomerService } from '../../service/customer.service';
 
@@ -7,7 +8,7 @@ export class CustomerController {
     constructor(private userService: CustomerService) {}
 
     @Get()
-    findAll(): Promise<Customer[]> {
+    findAll(): Promise<PaginatedList<Customer>> {
         return this.userService.findAll();
     }
 

+ 3 - 2
server/src/api/customer/customer.resolver.ts

@@ -1,4 +1,5 @@
 import { Mutation, Query, ResolveProperty, Resolver } from '@nestjs/graphql';
+import { PaginatedList } from '../../common/common-types';
 import { Address } from '../../entity/address/address.entity';
 import { CreateCustomerDto } from '../../entity/customer/customer.dto';
 import { Customer } from '../../entity/customer/customer.entity';
@@ -9,8 +10,8 @@ export class CustomerResolver {
     constructor(private customerService: CustomerService) {}
 
     @Query('customers')
-    customers(): Promise<Customer[]> {
-        return this.customerService.findAll();
+    customers(obj, args): Promise<PaginatedList<Customer>> {
+        return this.customerService.findAll(args.take, args.skip);
     }
 
     @Query('customer')

+ 6 - 1
server/src/api/product/product.api.graphql

@@ -1,5 +1,5 @@
 type Query {
-    products(languageCode: LanguageCode): [Product]
+    products(languageCode: LanguageCode, take: Int, skip: Int): ProductList
     product(id: Int!, languageCode: LanguageCode): Product
 }
 
@@ -9,3 +9,8 @@ type Mutation {
     "Update an existing Product"
     updateProduct(input: UpdateProductInput): Product
 }
+
+type ProductList implements PaginatedList {
+    items: [Product!]!
+    totalItems: Int!
+}

+ 3 - 3
server/src/api/product/product.resolver.ts

@@ -1,5 +1,5 @@
 import { Mutation, Query, Resolver } from '@nestjs/graphql';
-import { CreateProductDto } from '../../entity/product/product.dto';
+import { PaginatedList } from '../../common/common-types';
 import { Product } from '../../entity/product/product.entity';
 import { ProductVariantService } from '../../service/product-variant.service';
 import { ProductService } from '../../service/product.service';
@@ -9,8 +9,8 @@ export class ProductResolver {
     constructor(private productService: ProductService, private productVariantService: ProductVariantService) {}
 
     @Query('products')
-    products(obj, args): Promise<Product[]> {
-        return this.productService.findAll(args.languageCode);
+    products(obj, args): Promise<PaginatedList<Product>> {
+        return this.productService.findAll(args.languageCode, args.take, args.skip);
     }
 
     @Query('product')

+ 8 - 0
server/src/common/common-types.ts

@@ -10,3 +10,11 @@ export type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> };
 export type Type<T> = {
     new (): T;
 } & Function;
+
+/**
+ * A type describing the shape of a paginated list response
+ */
+export type PaginatedList<T> = {
+    items: T[];
+    totalItems: number;
+};

+ 9 - 2
server/src/service/customer.service.ts

@@ -3,6 +3,7 @@ import { InjectConnection } from '@nestjs/typeorm';
 import { Connection } from 'typeorm';
 import { PasswordService } from '../auth/password.service';
 import { Role } from '../auth/role';
+import { PaginatedList } from '../common/common-types';
 import { CreateAddressDto } from '../entity/address/address.dto';
 import { Address } from '../entity/address/address.entity';
 import { CreateCustomerDto } from '../entity/customer/customer.dto';
@@ -13,8 +14,14 @@ import { User } from '../entity/user/user.entity';
 export class CustomerService {
     constructor(@InjectConnection() private connection: Connection, private passwordService: PasswordService) {}
 
-    findAll(): Promise<Customer[]> {
-        return this.connection.manager.find(Customer);
+    findAll(take?: number, skip?: number): Promise<PaginatedList<Customer>> {
+        if (skip !== undefined && take === undefined) {
+            take = Number.MAX_SAFE_INTEGER;
+        }
+
+        return this.connection.manager
+            .findAndCount(Customer, { skip, take })
+            .then(([items, totalItems]) => ({ items, totalItems }));
     }
 
     findOne(userId: number): Promise<Customer | undefined> {

+ 15 - 6
server/src/service/product.service.ts

@@ -1,6 +1,7 @@
 import { Injectable } from '@nestjs/common';
 import { InjectConnection } from '@nestjs/typeorm';
 import { Connection } from 'typeorm';
+import { PaginatedList } from '../common/common-types';
 import { DEFAULT_LANGUAGE_CODE } from '../common/constants';
 import { ProductOptionGroup } from '../entity/product-option-group/product-option-group.entity';
 import { ProductTranslation } from '../entity/product/product-translation.entity';
@@ -18,16 +19,24 @@ export class ProductService {
         private translationUpdaterService: TranslationUpdaterService,
     ) {}
 
-    findAll(lang: LanguageCode): Promise<Product[]> {
+    findAll(lang: LanguageCode, take?: number, skip?: number): Promise<PaginatedList<Product>> {
         const relations = ['variants', 'optionGroups', 'variants.options'];
 
+        if (skip !== undefined && take === undefined) {
+            take = Number.MAX_SAFE_INTEGER;
+        }
+
         return this.connection.manager
-            .find(Product, { relations })
-            .then(products =>
-                products.map(product =>
+            .findAndCount(Product, { relations, take, skip })
+            .then(([products, totalItems]) => {
+                const items = products.map(product =>
                     translateDeep(product, lang, ['optionGroups', 'variants', ['variants', 'options']]),
-                ),
-            );
+                );
+                return {
+                    items,
+                    totalItems,
+                };
+            });
     }
 
     findOne(productId: number, lang: LanguageCode): Promise<Product | undefined> {