Browse Source

feat(server): Implement associating assets with products

Michael Bromley 7 years ago
parent
commit
b5d0264c7b

+ 63 - 0
server/e2e/product.e2e-spec.ts

@@ -9,6 +9,8 @@ import {
     GenerateProductVariants,
     GenerateProductVariants_generateVariantsForProduct_variants,
     GenerateProductVariantsVariables,
+    GetAssetList,
+    GetAssetListVariables,
     GetProductList,
     GetProductListVariables,
     GetProductWithVariants,
@@ -29,6 +31,7 @@ import {
     APPLY_FACET_VALUE_TO_PRODUCT_VARIANTS,
     CREATE_PRODUCT,
     GENERATE_PRODUCT_VARIANTS,
+    GET_ASSET_LIST,
     GET_PRODUCT_LIST,
     GET_PRODUCT_WITH_VARIANTS,
     REMOVE_OPTION_GROUP_FROM_PRODUCT,
@@ -39,6 +42,8 @@ import {
 import { TestClient } from './test-client';
 import { TestServer } from './test-server';
 
+// tslint:disable:no-non-null-assertion
+
 describe('Product resolver', () => {
     const client = new TestClient();
     const server = new TestServer();
@@ -166,6 +171,29 @@ describe('Product resolver', () => {
             expect(newProduct).toMatchSnapshot();
         });
 
+        it('createProduct creates a new Product with assets', async () => {
+            const assetsResult = await client.query<GetAssetList, GetAssetListVariables>(GET_ASSET_LIST);
+            const assetIds = assetsResult.assets.items.slice(0, 2).map(a => a.id);
+            const featuredAssetId = assetsResult.assets.items[0].id;
+
+            const result = await client.query<CreateProduct, CreateProductVariables>(CREATE_PRODUCT, {
+                input: {
+                    assetIds,
+                    featuredAssetId,
+                    translations: [
+                        {
+                            languageCode: LanguageCode.en,
+                            name: 'en Has Assets',
+                            slug: 'en-has-assets',
+                            description: 'A product with assets',
+                        },
+                    ],
+                },
+            });
+            expect(result.createProduct.assets.map(a => a.id)).toEqual(assetIds);
+            expect(result.createProduct.featuredAsset!.id).toBe(featuredAssetId);
+        });
+
         it('updateProduct updates a Product', async () => {
             const result = await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
                 input: {
@@ -207,6 +235,41 @@ describe('Product resolver', () => {
             expect(result.updateProduct.translations[1].name).toBe('de Mashed Potato');
         });
 
+        it('updateProduct adds Assets to a product and sets featured asset', async () => {
+            const assetsResult = await client.query<GetAssetList, GetAssetListVariables>(GET_ASSET_LIST);
+            const assetIds = assetsResult.assets.items.map(a => a.id);
+            const featuredAssetId = assetsResult.assets.items[2].id;
+
+            const result = await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
+                input: {
+                    id: newProduct.id,
+                    assetIds,
+                    featuredAssetId,
+                },
+            });
+            expect(result.updateProduct.assets.map(a => a.id)).toEqual(assetIds);
+            expect(result.updateProduct.featuredAsset!.id).toBe(featuredAssetId);
+        });
+
+        it('updateProduct sets a featured asset', async () => {
+            const productResult = await client.query<GetProductWithVariants, GetProductWithVariantsVariables>(
+                GET_PRODUCT_WITH_VARIANTS,
+                {
+                    id: newProduct.id,
+                    languageCode: LanguageCode.en,
+                },
+            );
+            const assets = productResult.product!.assets;
+
+            const result = await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {
+                input: {
+                    id: newProduct.id,
+                    featuredAssetId: assets[0].id,
+                },
+            });
+            expect(result.updateProduct.featuredAsset!.id).toBe(assets[0].id);
+        });
+
         it('updateProduct errors with an invalid productId', async () => {
             try {
                 await client.query<UpdateProduct, UpdateProductVariables>(UPDATE_PRODUCT, {

+ 1 - 1
server/mock-data/mock-data.service.ts

@@ -169,7 +169,7 @@ export class MockDataService {
         });
     }
 
-    async populateProducts(count: number = 5, optionGroupId: string): Promise<any> {
+    async populateProducts(count: number = 5, optionGroupId: string, assets: Asset[]): Promise<any> {
         for (let i = 0; i < count; i++) {
             const query = CREATE_PRODUCT;
 

+ 1 - 1
server/mock-data/populate.ts

@@ -41,7 +41,7 @@ export async function populate(
     }
     const assets = await mockDataService.populateAssets();
     const optionGroupId = await mockDataService.populateOptions();
-    await mockDataService.populateProducts(options.productCount, optionGroupId);
+    await mockDataService.populateProducts(options.productCount, optionGroupId, assets);
     await mockDataService.populateCustomers(options.customerCount);
     await mockDataService.populateFacets();
     return app;

+ 4 - 0
server/src/service/asset.service.ts

@@ -21,6 +21,10 @@ export class AssetService {
         return this.connection.getRepository(Asset).findOne(id);
     }
 
+    findByIds(ids: ID[]): Promise<Asset[]> {
+        return this.connection.getRepository(Asset).findByIds(ids);
+    }
+
     findAll(options?: ListQueryOptions<Asset>): Promise<PaginatedList<Asset>> {
         return buildListQuery(this.connection, Asset, options)
             .getManyAndCount()

+ 24 - 4
server/src/service/product.service.ts

@@ -13,6 +13,7 @@ import { ProductTranslation } from '../entity/product/product-translation.entity
 import { Product } from '../entity/product/product.entity';
 import { I18nError } from '../i18n/i18n-error';
 
+import { AssetService } from './asset.service';
 import { ChannelService } from './channel.service';
 import { buildListQuery } from './helpers/build-list-query';
 import { createTranslatable } from './helpers/create-translatable';
@@ -26,6 +27,7 @@ export class ProductService {
         @InjectConnection() private connection: Connection,
         private translationUpdaterService: TranslationUpdaterService,
         private channelService: ChannelService,
+        private assetService: AssetService,
     ) {}
 
     findAll(
@@ -83,17 +85,19 @@ export class ProductService {
         return this.applyChannelPriceToVariants(translated, ctx);
     }
 
-    async create(ctx: RequestContext, createProductDto: CreateProductInput): Promise<Translated<Product>> {
+    async create(ctx: RequestContext, input: CreateProductInput): Promise<Translated<Product>> {
         const save = createTranslatable(Product, ProductTranslation, async p => {
             this.channelService.assignToChannels(p, ctx);
         });
-        const product = await save(this.connection, createProductDto);
+        const product = await save(this.connection, input);
+        await this.saveAssetInputs(product, input);
         return assertFound(this.findOne(ctx, product.id));
     }
 
-    async update(ctx: RequestContext, updateProductDto: UpdateProductInput): Promise<Translated<Product>> {
+    async update(ctx: RequestContext, input: UpdateProductInput): Promise<Translated<Product>> {
         const save = updateTranslatable(Product, ProductTranslation, this.translationUpdaterService);
-        const product = await save(this.connection, updateProductDto);
+        const product = await save(this.connection, input);
+        await this.saveAssetInputs(product, input);
         return assertFound(this.findOne(ctx, product.id));
     }
 
@@ -133,6 +137,22 @@ export class ProductService {
         return assertFound(this.findOne(ctx, productId));
     }
 
+    private async saveAssetInputs(product: Product, input: CreateProductInput | UpdateProductInput) {
+        if (input.assetIds || input.featuredAssetId) {
+            if (input.assetIds) {
+                const assets = await this.assetService.findByIds(input.assetIds);
+                product.assets = assets;
+            }
+            if (input.featuredAssetId) {
+                const featuredAsset = await this.assetService.findOne(input.featuredAssetId);
+                if (featuredAsset) {
+                    product.featuredAsset = featuredAsset;
+                }
+            }
+            await this.connection.manager.save(product);
+        }
+    }
+
     private applyChannelPriceToVariants<T extends Product>(product: T, ctx: RequestContext): T {
         product.variants.forEach(v => {
             const channelPrice = v.productVariantPrices.find(p => idsAreEqual(p.channelId, ctx.channelId));