Browse Source

fix(core): Allow removal of all Assets from an entity

Michael Bromley 6 years ago
parent
commit
528eb3c5b0

+ 15 - 0
packages/core/e2e/collection.e2e-spec.ts

@@ -225,6 +225,21 @@ describe('Collection resolver', () => {
 
 
             expect(updateCollection.assets.map(a => a.id)).toEqual([assets[3].id, assets[0].id]);
             expect(updateCollection.assets.map(a => a.id)).toEqual([assets[3].id, assets[0].id]);
         });
         });
+
+        it('removes all assets', async () => {
+            const { updateCollection } = await client.query<
+                UpdateCollection.Mutation,
+                UpdateCollection.Variables
+            >(UPDATE_COLLECTION, {
+                input: {
+                    id: pearCollection.id,
+                    assetIds: [],
+                },
+            });
+
+            expect(updateCollection.assets).toEqual([]);
+            expect(updateCollection.featuredAsset).toBeNull();
+        });
     });
     });
 
 
     it('collection query', async () => {
     it('collection query', async () => {

+ 19 - 9
packages/core/src/service/services/asset.service.ts

@@ -21,10 +21,15 @@ import { VendureEntity } from '../../entity/base/base.entity';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 
 
 export interface EntityWithAssets extends VendureEntity {
 export interface EntityWithAssets extends VendureEntity {
-    featuredAsset: Asset;
+    featuredAsset: Asset | null;
     assets: OrderableAsset[];
     assets: OrderableAsset[];
 }
 }
 
 
+export interface EntityAssetInput {
+    assetIds?: ID[] | null;
+    featuredAssetId?: ID | null;
+}
+
 @Injectable()
 @Injectable()
 export class AssetService {
 export class AssetService {
     constructor(
     constructor(
@@ -68,7 +73,7 @@ export class AssetService {
             .findOne(entity.id, {
             .findOne(entity.id, {
                 relations: ['featuredAsset'],
                 relations: ['featuredAsset'],
             });
             });
-        return entityWithFeaturedAsset && entityWithFeaturedAsset.featuredAsset;
+        return (entityWithFeaturedAsset && entityWithFeaturedAsset.featuredAsset) || undefined;
     }
     }
 
 
     async getEntityAssets<T extends EntityWithAssets>(entity: T): Promise<Asset[] | undefined> {
     async getEntityAssets<T extends EntityWithAssets>(entity: T): Promise<Asset[] | undefined> {
@@ -85,11 +90,13 @@ export class AssetService {
         return assets.sort((a, b) => a.position - b.position).map(a => a.asset);
         return assets.sort((a, b) => a.position - b.position).map(a => a.asset);
     }
     }
 
 
-    async updateFeaturedAsset<T extends EntityWithAssets>(
-        entity: T,
-        featuredAssetId?: ID | null,
-    ): Promise<T> {
-        if (!featuredAssetId) {
+    async updateFeaturedAsset<T extends EntityWithAssets>(entity: T, input: EntityAssetInput): Promise<T> {
+        const { assetIds, featuredAssetId } = input;
+        if (featuredAssetId === null || (assetIds && assetIds.length === 0)) {
+            entity.featuredAsset = null;
+            return entity;
+        }
+        if (featuredAssetId === undefined) {
             return entity;
             return entity;
         }
         }
         const featuredAsset = await this.findOne(featuredAssetId);
         const featuredAsset = await this.findOne(featuredAssetId);
@@ -102,17 +109,20 @@ export class AssetService {
     /**
     /**
      * Updates the assets / featuredAsset of an entity, ensuring that only valid assetIds are used.
      * Updates the assets / featuredAsset of an entity, ensuring that only valid assetIds are used.
      */
      */
-    async updateEntityAssets<T extends EntityWithAssets>(entity: T, assetIds?: ID[] | null): Promise<T> {
+    async updateEntityAssets<T extends EntityWithAssets>(entity: T, input: EntityAssetInput): Promise<T> {
         if (!entity.id) {
         if (!entity.id) {
             throw new InternalServerError('error.entity-must-have-an-id');
             throw new InternalServerError('error.entity-must-have-an-id');
         }
         }
+        const { assetIds, featuredAssetId } = input;
         if (assetIds && assetIds.length) {
         if (assetIds && assetIds.length) {
             const assets = await this.connection.getRepository(Asset).findByIds(assetIds);
             const assets = await this.connection.getRepository(Asset).findByIds(assetIds);
             const sortedAssets = assetIds
             const sortedAssets = assetIds
                 .map(id => assets.find(a => idsAreEqual(a.id, id)))
                 .map(id => assets.find(a => idsAreEqual(a.id, id)))
                 .filter(notNullOrUndefined);
                 .filter(notNullOrUndefined);
-            this.removeExistingOrderableAssets(entity);
+            await this.removeExistingOrderableAssets(entity);
             entity.assets = await this.createOrderableAssets(entity, sortedAssets);
             entity.assets = await this.createOrderableAssets(entity, sortedAssets);
+        } else if (assetIds && assetIds.length === 0) {
+            await this.removeExistingOrderableAssets(entity);
         }
         }
         return entity;
         return entity;
     }
     }

+ 4 - 4
packages/core/src/service/services/collection.service.ts

@@ -250,10 +250,10 @@ export class CollectionService implements OnModuleInit {
                 }
                 }
                 coll.position = await this.getNextPositionInParent(ctx, input.parentId || undefined);
                 coll.position = await this.getNextPositionInParent(ctx, input.parentId || undefined);
                 coll.filters = this.getCollectionFiltersFromInput(input);
                 coll.filters = this.getCollectionFiltersFromInput(input);
-                await this.assetService.updateFeaturedAsset(coll, input.featuredAssetId);
+                await this.assetService.updateFeaturedAsset(coll, input);
             },
             },
         });
         });
-        await this.assetService.updateEntityAssets(collection, input.assetIds);
+        await this.assetService.updateEntityAssets(collection, input);
         this.applyCollectionFilters(ctx, [collection]);
         this.applyCollectionFilters(ctx, [collection]);
         return assertFound(this.findOne(ctx, collection.id));
         return assertFound(this.findOne(ctx, collection.id));
     }
     }
@@ -267,8 +267,8 @@ export class CollectionService implements OnModuleInit {
                 if (input.filters) {
                 if (input.filters) {
                     coll.filters = this.getCollectionFiltersFromInput(input);
                     coll.filters = this.getCollectionFiltersFromInput(input);
                 }
                 }
-                await this.assetService.updateFeaturedAsset(coll, input.featuredAssetId);
-                await this.assetService.updateEntityAssets(coll, input.assetIds);
+                await this.assetService.updateFeaturedAsset(coll, input);
+                await this.assetService.updateEntityAssets(coll, input);
             },
             },
         });
         });
         if (input.filters) {
         if (input.filters) {

+ 4 - 4
packages/core/src/service/services/product-variant.service.ts

@@ -176,14 +176,14 @@ export class ProductVariantService {
                 }
                 }
                 variant.product = { id: input.productId } as any;
                 variant.product = { id: input.productId } as any;
                 variant.taxCategory = { id: input.taxCategoryId } as any;
                 variant.taxCategory = { id: input.taxCategoryId } as any;
-                await this.assetService.updateFeaturedAsset(variant, input.featuredAssetId);
+                await this.assetService.updateFeaturedAsset(variant, input);
             },
             },
             typeOrmSubscriberData: {
             typeOrmSubscriberData: {
                 channelId: ctx.channelId,
                 channelId: ctx.channelId,
                 taxCategoryId: input.taxCategoryId,
                 taxCategoryId: input.taxCategoryId,
             },
             },
         });
         });
-        await this.assetService.updateEntityAssets(createdVariant, input.assetIds);
+        await this.assetService.updateEntityAssets(createdVariant, input);
         if (input.stockOnHand != null && input.stockOnHand !== 0) {
         if (input.stockOnHand != null && input.stockOnHand !== 0) {
             await this.stockMovementService.adjustProductVariantStock(
             await this.stockMovementService.adjustProductVariantStock(
                 createdVariant.id,
                 createdVariant.id,
@@ -227,8 +227,8 @@ export class ProductVariantService {
                         input.stockOnHand,
                         input.stockOnHand,
                     );
                     );
                 }
                 }
-                await this.assetService.updateFeaturedAsset(updatedVariant, input.featuredAssetId);
-                await this.assetService.updateEntityAssets(updatedVariant, input.assetIds);
+                await this.assetService.updateFeaturedAsset(updatedVariant, input);
+                await this.assetService.updateEntityAssets(updatedVariant, input);
             },
             },
             typeOrmSubscriberData: {
             typeOrmSubscriberData: {
                 channelId: ctx.channelId,
                 channelId: ctx.channelId,

+ 8 - 10
packages/core/src/service/services/product.service.ts

@@ -108,10 +108,10 @@ export class ProductService {
                 if (input.facetValueIds) {
                 if (input.facetValueIds) {
                     p.facetValues = await this.facetValueService.findByIds(input.facetValueIds);
                     p.facetValues = await this.facetValueService.findByIds(input.facetValueIds);
                 }
                 }
-                await this.assetService.updateFeaturedAsset(p, input.featuredAssetId);
+                await this.assetService.updateFeaturedAsset(p, input);
             },
             },
         });
         });
-        await this.assetService.updateEntityAssets(product, input.assetIds);
+        await this.assetService.updateEntityAssets(product, input);
         this.eventBus.publish(new CatalogModificationEvent(ctx, product));
         this.eventBus.publish(new CatalogModificationEvent(ctx, product));
         return assertFound(this.findOne(ctx, product.id));
         return assertFound(this.findOne(ctx, product.id));
     }
     }
@@ -127,8 +127,8 @@ export class ProductService {
                 if (input.facetValueIds) {
                 if (input.facetValueIds) {
                     p.facetValues = await this.facetValueService.findByIds(input.facetValueIds);
                     p.facetValues = await this.facetValueService.findByIds(input.facetValueIds);
                 }
                 }
-                await this.assetService.updateFeaturedAsset(p, input.featuredAssetId);
-                await this.assetService.updateEntityAssets(p, input.assetIds);
+                await this.assetService.updateFeaturedAsset(p, input);
+                await this.assetService.updateEntityAssets(p, input);
             },
             },
         });
         });
         this.eventBus.publish(new CatalogModificationEvent(ctx, product));
         this.eventBus.publish(new CatalogModificationEvent(ctx, product));
@@ -189,12 +189,10 @@ export class ProductService {
     }
     }
 
 
     private async getProductWithOptionGroups(productId: ID): Promise<Product> {
     private async getProductWithOptionGroups(productId: ID): Promise<Product> {
-        const product = await this.connection
-            .getRepository(Product)
-            .findOne(productId, {
-                relations: ['optionGroups', 'variants', 'variants.options'],
-                where: { deletedAt: null },
-            });
+        const product = await this.connection.getRepository(Product).findOne(productId, {
+            relations: ['optionGroups', 'variants', 'variants.options'],
+            where: { deletedAt: null },
+        });
         if (!product) {
         if (!product) {
             throw new EntityNotFoundError('Product', productId);
             throw new EntityNotFoundError('Product', productId);
         }
         }