Browse Source

Merge branch 'master' into minor

Michael Bromley 4 years ago
parent
commit
0823ca7b17
34 changed files with 473 additions and 93 deletions
  1. 14 0
      CHANGELOG.md
  2. 1 1
      docs/content/storefront/shop-api-guide.md
  3. 1 1
      lerna.json
  4. 3 3
      packages/admin-ui-plugin/package.json
  5. 2 2
      packages/admin-ui/package.json
  6. 1 1
      packages/admin-ui/src/lib/core/src/common/version.ts
  7. 3 3
      packages/asset-server-plugin/package.json
  8. 1 1
      packages/common/package.json
  9. 49 2
      packages/core/e2e/collection.e2e-spec.ts
  10. 17 0
      packages/core/e2e/default-search-plugin.e2e-spec.ts
  11. 26 0
      packages/core/e2e/entity-hydrator.e2e-spec.ts
  12. 16 0
      packages/core/e2e/fixtures/test-plugins/hydration-test-plugin.ts
  13. 30 0
      packages/core/e2e/graphql/generated-e2e-admin-types.ts
  14. 181 3
      packages/core/e2e/order-taxes.e2e-spec.ts
  15. 2 0
      packages/core/e2e/utils/assert-throws-with-message.ts
  16. 2 2
      packages/core/package.json
  17. 5 5
      packages/core/src/common/configurable-operation.ts
  18. 3 3
      packages/core/src/common/self-refreshing-cache.ts
  19. 1 1
      packages/core/src/plugin/default-search-plugin/search-strategy/mysql-search-strategy.ts
  20. 28 1
      packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts
  21. 1 1
      packages/core/src/service/helpers/product-price-applicator/product-price-applicator.ts
  22. 5 2
      packages/core/src/service/services/collection.service.ts
  23. 44 25
      packages/core/src/service/services/order.service.ts
  24. 2 1
      packages/core/src/service/services/product-variant.service.ts
  25. 2 2
      packages/core/src/service/services/product.service.ts
  26. 3 3
      packages/create/package.json
  27. 9 9
      packages/dev-server/package.json
  28. 3 3
      packages/elasticsearch-plugin/package.json
  29. 3 3
      packages/email-plugin/package.json
  30. 3 3
      packages/job-queue-plugin/package.json
  31. 4 4
      packages/payment-plugin/package.json
  32. 3 3
      packages/testing/package.json
  33. 1 1
      packages/testing/src/error-result-guard.ts
  34. 4 4
      packages/ui-devkit/package.json

+ 14 - 0
CHANGELOG.md

@@ -1,3 +1,17 @@
+## <small>1.3.4 (2021-11-23)</small>
+
+
+#### Fixes
+
+* **core** Correctly deep-merge hydrated entities ([32d19e3](https://github.com/vendure-ecommerce/vendure/commit/32d19e3)), closes [#1229](https://github.com/vendure-ecommerce/vendure/issues/1229)
+* **core** Correctly set OrderItem prices on tax zone change ([731f8d9](https://github.com/vendure-ecommerce/vendure/commit/731f8d9)), closes [#1216](https://github.com/vendure-ecommerce/vendure/issues/1216)
+* **core** Do not list deleted ProductVariants in Collections ([5cd8e1a](https://github.com/vendure-ecommerce/vendure/commit/5cd8e1a)), closes [#1213](https://github.com/vendure-ecommerce/vendure/issues/1213)
+* **core** Fix argsArrayToHash, case where arg not present in this.args (#1224) ([454fdf5](https://github.com/vendure-ecommerce/vendure/commit/454fdf5)), closes [#1224](https://github.com/vendure-ecommerce/vendure/issues/1224)
+* **core** Fix edge case FK error when creating new Collections ([160f457](https://github.com/vendure-ecommerce/vendure/commit/160f457)), closes [#1215](https://github.com/vendure-ecommerce/vendure/issues/1215)
+* **core** Fix ONLY_FULL_GROUP_BY error when searching with MySQL ([94fa4db](https://github.com/vendure-ecommerce/vendure/commit/94fa4db)), closes [#1236](https://github.com/vendure-ecommerce/vendure/issues/1236)
+* **core** Fix ProductService.assignProductsToChannel to properly assign assets to channel (#1235) ([a3066b0](https://github.com/vendure-ecommerce/vendure/commit/a3066b0)), closes [#1235](https://github.com/vendure-ecommerce/vendure/issues/1235)
+* **testing** Fix "fail is not defined" error ([c474d93](https://github.com/vendure-ecommerce/vendure/commit/c474d93))
+
 ## <small>1.3.3 (2021-11-09)</small>
 
 

+ 1 - 1
docs/content/storefront/shop-api-guide.md

@@ -97,7 +97,7 @@ Use the `product` query for the Product detail view.
 * {{< shop-api-operation operation="removeAllOrderLines" type="mutation" >}} removes all OrderLine from the Order.
 * {{< shop-api-operation operation="setCustomerForOrder" type="mutation" >}} specifies the Customer details (required if the customer is not logged in as an authenticated user).
 * {{< shop-api-operation operation="setOrderShippingAddress" type="mutation" >}} sets the shipping address for the Order.
-* {{< shop-api-operation operation="eligibleShippingMethods" type="mutation" >}} returns all available shipping methods based on the customer's shipping address and the contents of the Order.
+* {{< shop-api-operation operation="eligibleShippingMethods" type="query" >}} returns all available shipping methods based on the customer's shipping address and the contents of the Order.
 * {{< shop-api-operation operation="setOrderShippingMethod" type="mutation" >}} sets the shipping method to use.
 * {{< shop-api-operation operation="nextOrderStates" type="query" >}} returns the possible next states that the active Order can transition to
 * {{< shop-api-operation operation="transitionOrderToState" type="mutation" >}} transitions the active Order to the given state according to the [Order state machine]({{< relref "order-workflow" >}}).

+ 1 - 1
lerna.json

@@ -2,7 +2,7 @@
   "packages": [
     "packages/*"
   ],
-  "version": "1.3.3",
+  "version": "1.3.4",
   "npmClient": "yarn",
   "useWorkspaces": true,
   "command": {

+ 3 - 3
packages/admin-ui-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/admin-ui-plugin",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "main": "lib/index.js",
   "types": "lib/index.d.ts",
   "files": [
@@ -21,8 +21,8 @@
   "devDependencies": {
     "@types/express": "^4.17.8",
     "@types/fs-extra": "^9.0.1",
-    "@vendure/common": "^1.3.3",
-    "@vendure/core": "^1.3.3",
+    "@vendure/common": "^1.3.4",
+    "@vendure/core": "^1.3.4",
     "express": "^4.17.1",
     "rimraf": "^3.0.2",
     "typescript": "4.3.5"

+ 2 - 2
packages/admin-ui/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/admin-ui",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "license": "MIT",
   "scripts": {
     "ng": "ng",
@@ -39,7 +39,7 @@
     "@ng-select/ng-select": "^7.2.0",
     "@ngx-translate/core": "^13.0.0",
     "@ngx-translate/http-loader": "^6.0.0",
-    "@vendure/common": "^1.3.3",
+    "@vendure/common": "^1.3.4",
     "@webcomponents/custom-elements": "^1.4.3",
     "apollo-angular": "^2.4.0",
     "apollo-upload-client": "^14.1.3",

+ 1 - 1
packages/admin-ui/src/lib/core/src/common/version.ts

@@ -1,2 +1,2 @@
 // Auto-generated by the set-version.js script.
-export const ADMIN_UI_VERSION = '1.3.3';
+export const ADMIN_UI_VERSION = '1.3.4';

+ 3 - 3
packages/asset-server-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/asset-server-plugin",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "main": "lib/index.js",
   "types": "lib/index.d.ts",
   "files": [
@@ -24,8 +24,8 @@
     "@types/fs-extra": "^9.0.8",
     "@types/node-fetch": "^2.5.8",
     "@types/sharp": "^0.27.1",
-    "@vendure/common": "^1.3.3",
-    "@vendure/core": "^1.3.3",
+    "@vendure/common": "^1.3.4",
+    "@vendure/core": "^1.3.4",
     "aws-sdk": "^2.856.0",
     "express": "^4.17.1",
     "node-fetch": "^2.6.1",

+ 1 - 1
packages/common/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/common",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "main": "index.js",
   "license": "MIT",
   "scripts": {

+ 49 - 2
packages/core/e2e/collection.e2e-spec.ts

@@ -21,6 +21,7 @@ import {
     CreateCollectionSelectVariants,
     DeleteCollection,
     DeleteProduct,
+    DeleteProductVariant,
     DeletionResult,
     FacetValueFragment,
     GetAssetList,
@@ -45,6 +46,7 @@ import {
 import {
     CREATE_COLLECTION,
     DELETE_PRODUCT,
+    DELETE_PRODUCT_VARIANT,
     GET_ASSET_LIST,
     UPDATE_COLLECTION,
     UPDATE_PRODUCT,
@@ -1548,7 +1550,6 @@ describe('Collection resolver', () => {
         });
 
         it('filter inheritance of nested collections (issue #158)', async () => {
-            const a = 1;
             const { createCollection: pearElectronics } = await adminClient.query<
                 CreateCollectionSelectVariants.Mutation,
                 CreateCollectionSelectVariants.Variables
@@ -1649,6 +1650,7 @@ describe('Collection resolver', () => {
             await adminClient.query<DeleteProduct.Mutation, DeleteProduct.Variables>(DELETE_PRODUCT, {
                 id: 'T_2', // curvy monitor
             });
+            await awaitRunningJobs(adminClient, 5000);
             const { collection } = await adminClient.query<
                 GetCollectionProducts.Query,
                 GetCollectionProducts.Variables
@@ -1665,6 +1667,31 @@ describe('Collection resolver', () => {
             ]);
         });
 
+        // https://github.com/vendure-ecommerce/vendure/issues/1213
+        it('does not list deleted variants', async () => {
+            await adminClient.query<DeleteProductVariant.Mutation, DeleteProductVariant.Variables>(
+                DELETE_PRODUCT_VARIANT,
+                {
+                    id: 'T_18', // Instant Camera
+                },
+            );
+            await awaitRunningJobs(adminClient, 5000);
+            const { collection } = await adminClient.query<
+                GetCollectionProducts.Query,
+                GetCollectionProducts.Variables
+            >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                id: pearCollection.id,
+            });
+            expect(collection!.productVariants.items.map(i => i.name)).toEqual([
+                'Laptop 13 inch 8GB',
+                'Laptop 15 inch 8GB',
+                'Laptop 13 inch 16GB',
+                'Laptop 15 inch 16GB',
+                'Gaming PC i7-8700 240GB SSD',
+                // 'Instant Camera',
+            ]);
+        });
+
         it('does not list disabled variants in Shop API', async () => {
             await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
                 UPDATE_PRODUCT_VARIANTS,
@@ -1683,7 +1710,28 @@ describe('Collection resolver', () => {
             expect(collection!.productVariants.items.map(i => i.id).includes('T_1')).toBe(false);
         });
 
+        it('does not list variants of disabled products in Shop API', async () => {
+            await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
+                input: { id: 'T_1', enabled: false },
+            });
+            await awaitRunningJobs(adminClient, 5000);
+
+            const { collection } = await shopClient.query<
+                GetCollectionProducts.Query,
+                GetCollectionProducts.Variables
+            >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                id: pearCollection.id,
+            });
+            expect(collection!.productVariants.items.map(i => i.id).includes('T_1')).toBe(false);
+            expect(collection!.productVariants.items.map(i => i.id).includes('T_2')).toBe(false);
+            expect(collection!.productVariants.items.map(i => i.id).includes('T_3')).toBe(false);
+            expect(collection!.productVariants.items.map(i => i.id).includes('T_4')).toBe(false);
+        });
+
         it('handles other languages', async () => {
+            await adminClient.query<UpdateProduct.Mutation, UpdateProduct.Variables>(UPDATE_PRODUCT, {
+                input: { id: 'T_1', enabled: true },
+            });
             await adminClient.query<UpdateProductVariants.Mutation, UpdateProductVariants.Variables>(
                 UPDATE_PRODUCT_VARIANTS,
                 {
@@ -1710,7 +1758,6 @@ describe('Collection resolver', () => {
                 'Laptop 13 inch 16GB',
                 'Laptop 15 inch 16GB',
                 'Gaming PC i7-8700 240GB SSD',
-                'Instant Camera',
             ]);
         });
     });

+ 17 - 0
packages/core/e2e/default-search-plugin.e2e-spec.ts

@@ -524,6 +524,23 @@ describe('Default search plugin', () => {
             ]);
         });
 
+        // https://github.com/vendure-ecommerce/vendure/issues/1236
+        it('returns correct facetValues when not grouped by product, with search term', async () => {
+            const result = await shopClient.query<SearchFacetValues.Query, SearchFacetValues.Variables>(
+                SEARCH_GET_FACET_VALUES,
+                {
+                    input: {
+                        groupByProduct: false,
+                        term: 'laptop',
+                    },
+                },
+            );
+            expect(result.search.facetValues).toEqual([
+                { count: 4, facetValue: { id: 'T_1', name: 'electronics' } },
+                { count: 4, facetValue: { id: 'T_2', name: 'computers' } },
+            ]);
+        });
+
         it('omits facetValues of private facets', async () => {
             const { createFacet } = await adminClient.query<CreateFacet.Mutation, CreateFacet.Variables>(
                 CREATE_FACET,

+ 26 - 0
packages/core/e2e/entity-hydrator.e2e-spec.ts

@@ -167,6 +167,27 @@ describe('Entity hydration', () => {
         expect(hydrateOrder.id).toBe('T_1');
         expect(hydrateOrder.payments).toEqual([]);
     });
+
+    // https://github.com/vendure-ecommerce/vendure/issues/1229
+    it('deep merges existing properties', async () => {
+        await shopClient.asAnonymousUser();
+        const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(
+            ADD_ITEM_TO_ORDER,
+            {
+                productVariantId: 'T_1',
+                quantity: 2,
+            },
+        );
+        orderResultGuard.assertSuccess(addItemToOrder);
+
+        const { hydrateOrderReturnQuantities } = await adminClient.query<{
+            hydrateOrderReturnQuantities: number[];
+        }>(GET_HYDRATED_ORDER_QUANTITIES, {
+            id: addItemToOrder.id,
+        });
+
+        expect(hydrateOrderReturnQuantities).toEqual([2]);
+    });
 });
 
 function getVariantWithName(product: Product, name: string) {
@@ -195,3 +216,8 @@ const GET_HYDRATED_ORDER = gql`
         hydrateOrder(id: $id)
     }
 `;
+const GET_HYDRATED_ORDER_QUANTITIES = gql`
+    query GetHydratedOrderQuantities($id: ID!) {
+        hydrateOrderReturnQuantities(id: $id)
+    }
+`;

+ 16 - 0
packages/core/e2e/fixtures/test-plugins/hydration-test-plugin.ts

@@ -73,6 +73,21 @@ export class TestAdminPluginResolver {
         });
         return order;
     }
+
+    // Test case for https://github.com/vendure-ecommerce/vendure/issues/1229
+    @Query()
+    async hydrateOrderReturnQuantities(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
+        const order = await this.orderService.findOne(ctx, args.id);
+        await this.entityHydrator.hydrate(ctx, order!, {
+            relations: [
+                'lines',
+                'lines.productVariant',
+                'lines.productVariant.product',
+                'lines.productVariant.product.assets',
+            ],
+        });
+        return order?.lines.map(line => line.quantity);
+    }
 }
 
 @VendurePlugin({
@@ -85,6 +100,7 @@ export class TestAdminPluginResolver {
                 hydrateProductAsset(id: ID!): JSON
                 hydrateProductVariant(id: ID!): JSON
                 hydrateOrder(id: ID!): JSON
+                hydrateOrderReturnQuantities(id: ID!): JSON
             }
         `,
     },

+ 30 - 0
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -6405,6 +6405,21 @@ export type DeletePromotionAdHoc1MutationVariables = Exact<{ [key: string]: neve
 
 export type DeletePromotionAdHoc1Mutation = { deletePromotion: Pick<DeletionResponse, 'result'> };
 
+export type GetTaxRateListQueryVariables = Exact<{
+    options?: Maybe<TaxRateListOptions>;
+}>;
+
+export type GetTaxRateListQuery = {
+    taxRates: Pick<TaxRateList, 'totalItems'> & {
+        items: Array<
+            Pick<TaxRate, 'id' | 'name' | 'enabled' | 'value'> & {
+                category: Pick<TaxCategory, 'id' | 'name'>;
+                zone: Pick<Zone, 'id' | 'name'>;
+            }
+        >;
+    };
+};
+
 export type GetOrderListFulfillmentsQueryVariables = Exact<{ [key: string]: never }>;
 
 export type GetOrderListFulfillmentsQuery = {
@@ -8738,6 +8753,21 @@ export namespace DeletePromotionAdHoc1 {
     export type DeletePromotion = NonNullable<DeletePromotionAdHoc1Mutation['deletePromotion']>;
 }
 
+export namespace GetTaxRateList {
+    export type Variables = GetTaxRateListQueryVariables;
+    export type Query = GetTaxRateListQuery;
+    export type TaxRates = NonNullable<GetTaxRateListQuery['taxRates']>;
+    export type Items = NonNullable<
+        NonNullable<NonNullable<GetTaxRateListQuery['taxRates']>['items']>[number]
+    >;
+    export type Category = NonNullable<
+        NonNullable<NonNullable<NonNullable<GetTaxRateListQuery['taxRates']>['items']>[number]>['category']
+    >;
+    export type Zone = NonNullable<
+        NonNullable<NonNullable<NonNullable<GetTaxRateListQuery['taxRates']>['items']>[number]>['zone']
+    >;
+}
+
 export namespace GetOrderListFulfillments {
     export type Variables = GetOrderListFulfillmentsQueryVariables;
     export type Query = GetOrderListFulfillmentsQuery;

+ 181 - 3
packages/core/e2e/order-taxes.e2e-spec.ts

@@ -1,26 +1,96 @@
 /* tslint:disable:no-non-null-assertion */
 import { summate } from '@vendure/common/lib/shared-utils';
+import {
+    Channel,
+    Injector,
+    Order,
+    RequestContext,
+    TaxZoneStrategy,
+    TransactionalConnection,
+    Zone,
+} from '@vendure/core';
 import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
+import gql from 'graphql-tag';
 import path from 'path';
 
 import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
 
 import { testSuccessfulPaymentMethod } from './fixtures/test-payment-methods';
-import { GetProductsWithVariantPrices, UpdateChannel } from './graphql/generated-e2e-admin-types';
+import {
+    GetProductsWithVariantPrices,
+    GetTaxRateList,
+    UpdateChannel,
+    UpdateTaxRate,
+} from './graphql/generated-e2e-admin-types';
 import {
     AddItemToOrder,
     GetActiveOrderWithPriceData,
+    SetBillingAddress,
+    SetShippingAddress,
     TestOrderFragmentFragment,
     UpdatedOrderFragment,
 } from './graphql/generated-e2e-shop-types';
-import { GET_PRODUCTS_WITH_VARIANT_PRICES, UPDATE_CHANNEL } from './graphql/shared-definitions';
-import { ADD_ITEM_TO_ORDER, GET_ACTIVE_ORDER_WITH_PRICE_DATA } from './graphql/shop-definitions';
+import {
+    GET_PRODUCTS_WITH_VARIANT_PRICES,
+    UPDATE_CHANNEL,
+    UPDATE_TAX_RATE,
+} from './graphql/shared-definitions';
+import {
+    ADD_ITEM_TO_ORDER,
+    GET_ACTIVE_ORDER_WITH_PRICE_DATA,
+    SET_BILLING_ADDRESS,
+    SET_SHIPPING_ADDRESS,
+} from './graphql/shop-definitions';
 import { sortById } from './utils/test-order-utils';
 
+/**
+ * Determines active tax zone based on:
+ *
+ * 1. billingAddress country, if set
+ * 2. else shippingAddress country, is set
+ * 3. else channel default tax zone.
+ */
+class TestTaxZoneStrategy implements TaxZoneStrategy {
+    private connection: TransactionalConnection;
+
+    init(injector: Injector): void | Promise<void> {
+        this.connection = injector.get(TransactionalConnection);
+    }
+
+    async determineTaxZone(
+        ctx: RequestContext,
+        zones: Zone[],
+        channel: Channel,
+        order?: Order,
+    ): Promise<Zone> {
+        if (!order?.billingAddress?.countryCode && !order?.shippingAddress?.countryCode) {
+            return channel.defaultTaxZone;
+        }
+
+        const countryCode = order?.billingAddress?.countryCode || order?.shippingAddress?.countryCode;
+        const zoneForCountryCode = await this.getZoneForCountryCode(ctx, countryCode);
+        return zoneForCountryCode ?? channel.defaultTaxZone;
+    }
+
+    private getZoneForCountryCode(ctx: RequestContext, countryCode: string): Promise<Zone | undefined> {
+        return this.connection
+            .getRepository(ctx, Zone)
+            .createQueryBuilder('zone')
+            .leftJoin('zone.members', 'country')
+            .where('country.code = :countryCode', {
+                countryCode,
+            })
+            .getOne();
+    }
+}
+
 describe('Order taxes', () => {
     const { server, adminClient, shopClient } = createTestEnvironment({
         ...testConfig(),
+        taxOptions: {
+            taxZoneStrategy: new TestTaxZoneStrategy(),
+        },
         paymentOptions: {
             paymentMethodHandlers: [testSuccessfulPaymentMethod],
         },
@@ -137,6 +207,92 @@ describe('Order taxes', () => {
                 },
             ]);
         });
+
+        // https://github.com/vendure-ecommerce/vendure/issues/1216
+        it('re-calculates OrderItem prices when shippingAddress causes activeTaxZone change', async () => {
+            const { taxRates } = await adminClient.query<GetTaxRateList.Query>(GET_TAX_RATE_LIST);
+            // Set the TaxRates to Asia to 0%
+            const taxRatesAsia = taxRates.items.filter(tr => tr.name.includes('Asia'));
+            for (const taxRate of taxRatesAsia) {
+                await adminClient.query<UpdateTaxRate.Mutation, UpdateTaxRate.Variables>(UPDATE_TAX_RATE, {
+                    input: {
+                        id: taxRate.id,
+                        value: 0,
+                    },
+                });
+            }
+
+            await shopClient.query<SetShippingAddress.Mutation, SetShippingAddress.Variables>(
+                SET_SHIPPING_ADDRESS,
+                {
+                    input: {
+                        countryCode: 'CN',
+                        streetLine1: '123 Lugu St',
+                        city: 'Beijing',
+                        province: 'Beijing',
+                        postalCode: '12340',
+                    },
+                },
+            );
+
+            const { activeOrder } = await shopClient.query<GetActiveOrderWithPriceData.Query>(
+                GET_ACTIVE_ORDER_WITH_PRICE_DATA,
+            );
+            expect(activeOrder?.totalWithTax).toBe(166);
+            expect(activeOrder?.total).toBe(166);
+            expect(activeOrder?.lines[0].taxRate).toBe(0);
+            expect(activeOrder?.lines[0].linePrice).toBe(166);
+            expect(activeOrder?.lines[0].lineTax).toBe(0);
+            expect(activeOrder?.lines[0].linePriceWithTax).toBe(166);
+            expect(activeOrder?.lines[0].unitPrice).toBe(83);
+            expect(activeOrder?.lines[0].unitPriceWithTax).toBe(83);
+            expect(activeOrder?.lines[0].items[0].unitPrice).toBe(83);
+            expect(activeOrder?.lines[0].items[0].unitPriceWithTax).toBe(83);
+            expect(activeOrder?.lines[0].items[0].taxRate).toBe(0);
+            expect(activeOrder?.lines[0].taxLines).toEqual([
+                {
+                    description: 'Standard Tax Asia',
+                    taxRate: 0,
+                },
+            ]);
+        });
+
+        // https://github.com/vendure-ecommerce/vendure/issues/1216
+        it('re-calculates OrderItem prices when billingAddress causes activeTaxZone change', async () => {
+            await shopClient.query<SetBillingAddress.Mutation, SetBillingAddress.Variables>(
+                SET_BILLING_ADDRESS,
+                {
+                    input: {
+                        countryCode: 'US',
+                        streetLine1: '123 Chad Street',
+                        city: 'Houston',
+                        province: 'Texas',
+                        postalCode: '12345',
+                    },
+                },
+            );
+
+            const { activeOrder } = await shopClient.query<GetActiveOrderWithPriceData.Query>(
+                GET_ACTIVE_ORDER_WITH_PRICE_DATA,
+            );
+            expect(activeOrder?.totalWithTax).toBe(200);
+            expect(activeOrder?.total).toBe(166);
+            expect(activeOrder?.lines[0].taxRate).toBe(20);
+            expect(activeOrder?.lines[0].linePrice).toBe(166);
+            expect(activeOrder?.lines[0].lineTax).toBe(34);
+            expect(activeOrder?.lines[0].linePriceWithTax).toBe(200);
+            expect(activeOrder?.lines[0].unitPrice).toBe(83);
+            expect(activeOrder?.lines[0].unitPriceWithTax).toBe(100);
+            expect(activeOrder?.lines[0].items[0].unitPrice).toBe(83);
+            expect(activeOrder?.lines[0].items[0].unitPriceWithTax).toBe(100);
+            expect(activeOrder?.lines[0].items[0].taxRate).toBe(20);
+            expect(activeOrder?.lines[0].taxLines).toEqual([
+                {
+                    description: 'Standard Tax Americas',
+                    taxRate: 20,
+                },
+            ]);
+        });
     });
 
     it('taxSummary works', async () => {
@@ -193,3 +349,25 @@ describe('Order taxes', () => {
         expect(taxSummaryBaseTotal + taxSummaryTaxTotal).toBe(activeOrder?.totalWithTax);
     });
 });
+
+export const GET_TAX_RATE_LIST = gql`
+    query GetTaxRateList($options: TaxRateListOptions) {
+        taxRates(options: $options) {
+            items {
+                id
+                name
+                enabled
+                value
+                category {
+                    id
+                    name
+                }
+                zone {
+                    id
+                    name
+                }
+            }
+            totalItems
+        }
+    }
+`;

+ 2 - 0
packages/core/e2e/utils/assert-throws-with-message.ts

@@ -1,3 +1,5 @@
+import { fail } from 'assert';
+
 /**
  * Helper method for creating tests which assert a given error message when the operation is attempted.
  */

+ 2 - 2
packages/core/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/core",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "description": "A modern, headless ecommerce framework",
   "repository": {
     "type": "git",
@@ -49,7 +49,7 @@
     "@nestjs/testing": "7.6.17",
     "@nestjs/typeorm": "7.1.5",
     "@types/fs-extra": "^9.0.1",
-    "@vendure/common": "^1.3.3",
+    "@vendure/common": "^1.3.4",
     "apollo-server-express": "2.24.1",
     "bcrypt": "^5.0.0",
     "body-parser": "^1.19.0",

+ 5 - 5
packages/core/src/common/configurable-operation.ts

@@ -45,9 +45,9 @@ export type UiComponentConfig =
     | ({ component: 'boolean-form-input' } & DefaultFormComponentConfig<'boolean-form-input'>)
     | ({ component: 'currency-form-input' } & DefaultFormComponentConfig<'currency-form-input'>)
     | ({ component: 'facet-value-form-input' } & DefaultFormComponentConfig<'facet-value-form-input'>)
-    | ({ component: 'product-selector-form-input' } & DefaultFormComponentConfig<
-          'product-selector-form-input'
-      >)
+    | ({
+          component: 'product-selector-form-input';
+      } & DefaultFormComponentConfig<'product-selector-form-input'>)
     | ({ component: 'customer-group-form-input' } & DefaultFormComponentConfig<'customer-group-form-input'>)
     | { component: string; [prop: string]: any };
 
@@ -63,7 +63,7 @@ export interface ConfigArgCommonDef<T extends ConfigArgType> {
 
 export type ConfigArgListDef<
     T extends ConfigArgType,
-    C extends ConfigArgCommonDef<T> = ConfigArgCommonDef<T>
+    C extends ConfigArgCommonDef<T> = ConfigArgCommonDef<T>,
 > = C & { list: true };
 
 export type WithArgConfig<T> = {
@@ -402,7 +402,7 @@ export class ConfigurableOperationDef<T extends ConfigArgs = ConfigArgs> {
     protected argsArrayToHash(args: ConfigArg[]): ConfigArgValues<T> {
         const output: ConfigArgValues<T> = {} as any;
         for (const arg of args) {
-            if (arg && arg.value != null) {
+            if (arg && arg.value != null && this.args[arg.name] != null) {
                 output[arg.name as keyof ConfigArgValues<T>] = coerceValueToType<T>(
                     arg.value,
                     this.args[arg.name].type,

+ 3 - 3
packages/core/src/common/self-refreshing-cache.ts

@@ -46,9 +46,9 @@ export interface SelfRefreshingCacheConfig<V, RefreshArgs extends any[]> {
  * of cache, the function used to populate the value (`refreshFn`) is defined during the creation of the cache, and
  * it is immediately used to populate the initial value.
  *
- * From there, when the `.value` property is accessed, it will _always_ return a value synchronously. If the
- * value has expired, it will still be returned and the `refreshFn` will be triggered to update the value in the
- * background.
+ * From there, when the `.value` property is accessed, it will return a value from the cache, and if the
+ * value has expired, it will automatically run the `refreshFn` to update the value and then return the
+ * fresh value.
  */
 export async function createSelfRefreshingCache<V, RefreshArgs extends any[]>(
     config: SelfRefreshingCacheConfig<V, RefreshArgs>,

+ 1 - 1
packages/core/src/plugin/default-search-plugin/search-strategy/mysql-search-strategy.ts

@@ -38,7 +38,7 @@ export class MysqlSearchStrategy implements SearchStrategy {
             .select(['MIN(productId)', 'MIN(productVariantId)'])
             .addSelect('GROUP_CONCAT(facetValueIds)', 'facetValues');
 
-        this.applyTermAndFilters(ctx, facetValuesQb, input);
+        this.applyTermAndFilters(ctx, facetValuesQb, { ...input, groupByProduct: true });
         if (!input.groupByProduct) {
             facetValuesQb.groupBy('productVariantId');
         }

+ 28 - 1
packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts

@@ -1,5 +1,6 @@
 import { Injectable } from '@nestjs/common';
 import { Type } from '@vendure/common/lib/shared-types';
+import { isObject } from '@vendure/common/lib/shared-utils';
 import { unique } from '@vendure/common/lib/unique';
 
 import { RequestContext } from '../../../api/common/request-context';
@@ -89,7 +90,7 @@ export class EntityHydrator {
                     });
                 const propertiesToAdd = unique(missingRelations.map(relation => relation.split('.')[0]));
                 for (const prop of propertiesToAdd) {
-                    (target as any)[prop] = (hydrated as any)[prop];
+                    (target as any)[prop] = this.mergeDeep((target as any)[prop], (hydrated as any)[prop]);
                 }
 
                 const relationsWithEntities = missingRelations.map(relation => ({
@@ -241,4 +242,30 @@ export class EntityHydrator {
             ? input[0]?.hasOwnProperty('translations') ?? false
             : input?.hasOwnProperty('translations') ?? false;
     }
+
+    /**
+     * Merges properties into a target entity. This is needed for the cases in which a
+     * property already exists on the target, but the hydrated version also contains that
+     * property with a different set of properties. This prevents the original target
+     * entity from having data overwritten.
+     */
+    private mergeDeep<T extends { [key: string]: any }>(a: T | undefined, b: T): T {
+        if (!a) {
+            return b;
+        }
+        for (const [key, value] of Object.entries(b)) {
+            if (Object.getOwnPropertyDescriptor(b, key)?.writable) {
+                if (Array.isArray(value)) {
+                    (a as any)[key] = value.map((v, index) =>
+                        this.mergeDeep(a?.[key]?.[index], b[key][index]),
+                    );
+                } else if (isObject(value)) {
+                    (a as any)[key] = this.mergeDeep(a?.[key], b[key]);
+                } else {
+                    (a as any)[key] = b[key];
+                }
+            }
+        }
+        return a ?? b;
+    }
 }

+ 1 - 1
packages/core/src/service/helpers/product-price-applicator/product-price-applicator.ts

@@ -42,7 +42,7 @@ export class ProductPriceApplicator {
         }
         const { taxZoneStrategy } = this.configService.taxOptions;
         const zones = await this.requestCache.get(ctx, 'allZones', () => this.zoneService.findAll(ctx));
-        const activeTaxZone = await this.requestCache.get(ctx, 'activeTaxZone', () =>
+        const activeTaxZone = await this.requestCache.get(ctx, `activeTaxZone`, () =>
             taxZoneStrategy.determineTaxZone(ctx, zones, ctx.channel, order),
         );
         if (!activeTaxZone) {

+ 5 - 2
packages/core/src/service/services/collection.service.ts

@@ -622,7 +622,10 @@ export class CollectionService implements OnModuleInit {
             return this.rootCollection;
         }
 
-        const rootTranslation = await this.connection.getRepository(ctx, CollectionTranslation).save(
+        // We purposefully do not use the ctx in saving the new root Collection
+        // so that even if the outer transaction fails, the root collection will still
+        // get persisted.
+        const rootTranslation = await this.connection.getRepository(CollectionTranslation).save(
             new CollectionTranslation({
                 languageCode: this.configService.defaultLanguageCode,
                 name: ROOT_COLLECTION_NAME,
@@ -631,7 +634,7 @@ export class CollectionService implements OnModuleInit {
             }),
         );
 
-        const newRoot = await this.connection.getRepository(ctx, Collection).save(
+        const newRoot = await this.connection.getRepository(Collection).save(
             new Collection({
                 isRoot: true,
                 position: 0,

+ 44 - 25
packages/core/src/service/services/order.service.ts

@@ -40,6 +40,7 @@ import { unique } from '@vendure/common/lib/unique';
 import { FindOptionsUtils } from 'typeorm/find-options/FindOptionsUtils';
 
 import { RequestContext } from '../../api/common/request-context';
+import { RequestContextCacheService } from '../../cache/request-context-cache.service';
 import { ErrorResultUnion, isGraphQlErrorResult } from '../../common/error/error-result';
 import { EntityNotFoundError, InternalServerError, UserInputError } from '../../common/error/errors';
 import {
@@ -155,6 +156,7 @@ export class OrderService {
         private channelService: ChannelService,
         private orderModifier: OrderModifier,
         private customFieldRelationService: CustomFieldRelationService,
+        private requestCache: RequestContextCacheService,
     ) {}
 
     /**
@@ -460,7 +462,7 @@ export class OrderService {
             await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
         }
         const quantityWasAdjustedDown = correctedQuantity < quantity;
-        const updatedOrder = await this.applyPriceAdjustments(ctx, order, orderLine);
+        const updatedOrder = await this.applyPriceAdjustments(ctx, order, [orderLine]);
         if (quantityWasAdjustedDown) {
             return new InsufficientStockError(correctedQuantity, updatedOrder);
         } else {
@@ -510,7 +512,7 @@ export class OrderService {
             await this.orderModifier.updateOrderLineQuantity(ctx, orderLine, correctedQuantity, order);
         }
         const quantityWasAdjustedDown = correctedQuantity < quantity;
-        const updatedOrder = await this.applyPriceAdjustments(ctx, order, orderLine);
+        const updatedOrder = await this.applyPriceAdjustments(ctx, order, [orderLine]);
         if (quantityWasAdjustedDown) {
             return new InsufficientStockError(correctedQuantity, updatedOrder);
         } else {
@@ -694,7 +696,12 @@ export class OrderService {
         const order = await this.getOrderOrThrow(ctx, orderId);
         const country = await this.countryService.findOneByCode(ctx, input.countryCode);
         order.shippingAddress = { ...input, countryCode: input.countryCode, country: country.name };
-        return this.connection.getRepository(ctx, Order).save(order);
+        await this.connection.getRepository(ctx, Order).save(order);
+        // Since a changed ShippingAddress could alter the activeTaxZone,
+        // we will remove any cached activeTaxZone so it can be re-calculated
+        // as needed.
+        this.requestCache.set(ctx, 'activeTaxZone', undefined);
+        return this.applyPriceAdjustments(ctx, order, order.lines);
     }
 
     /**
@@ -705,7 +712,12 @@ export class OrderService {
         const order = await this.getOrderOrThrow(ctx, orderId);
         const country = await this.countryService.findOneByCode(ctx, input.countryCode);
         order.billingAddress = { ...input, countryCode: input.countryCode, country: country.name };
-        return this.connection.getRepository(ctx, Order).save(order);
+        await this.connection.getRepository(ctx, Order).save(order);
+        // Since a changed ShippingAddress could alter the activeTaxZone,
+        // we will remove any cached activeTaxZone so it can be re-calculated
+        // as needed.
+        this.requestCache.set(ctx, 'activeTaxZone', undefined);
+        return this.applyPriceAdjustments(ctx, order, order.lines);
     }
 
     /**
@@ -1533,32 +1545,39 @@ export class OrderService {
     private async applyPriceAdjustments(
         ctx: RequestContext,
         order: Order,
-        updatedOrderLine?: OrderLine,
+        updatedOrderLines?: OrderLine[],
     ): Promise<Order> {
-        if (updatedOrderLine) {
+        if (updatedOrderLines?.length) {
             const { orderItemPriceCalculationStrategy, changedPriceHandlingStrategy } =
                 this.configService.orderOptions;
-            let priceResult = await orderItemPriceCalculationStrategy.calculateUnitPrice(
-                ctx,
-                updatedOrderLine.productVariant,
-                updatedOrderLine.customFields || {},
-            );
-            const initialListPrice =
-                updatedOrderLine.items.find(i => i.initialListPrice != null)?.initialListPrice ??
-                priceResult.price;
-            if (initialListPrice !== priceResult.price) {
-                priceResult = await changedPriceHandlingStrategy.handlePriceChange(
+            for (const updatedOrderLine of updatedOrderLines) {
+                const variant = await this.productVariantService.applyChannelPriceAndTax(
+                    updatedOrderLine.productVariant,
                     ctx,
-                    priceResult,
-                    updatedOrderLine.items,
+                    order,
                 );
-            }
-            for (const item of updatedOrderLine.items) {
-                if (item.initialListPrice == null) {
-                    item.initialListPrice = initialListPrice;
+                let priceResult = await orderItemPriceCalculationStrategy.calculateUnitPrice(
+                    ctx,
+                    variant,
+                    updatedOrderLine.customFields || {},
+                );
+                const initialListPrice =
+                    updatedOrderLine.items.find(i => i.initialListPrice != null)?.initialListPrice ??
+                    priceResult.price;
+                if (initialListPrice !== priceResult.price) {
+                    priceResult = await changedPriceHandlingStrategy.handlePriceChange(
+                        ctx,
+                        priceResult,
+                        updatedOrderLine.items,
+                    );
+                }
+                for (const item of updatedOrderLine.items) {
+                    if (item.initialListPrice == null) {
+                        item.initialListPrice = initialListPrice;
+                    }
+                    item.listPrice = priceResult.price;
+                    item.listPriceIncludesTax = priceResult.priceIncludesTax;
                 }
-                item.listPrice = priceResult.price;
-                item.listPriceIncludesTax = priceResult.priceIncludesTax;
             }
         }
         const { items: promotions } = await this.promotionService.findAll(ctx, {
@@ -1569,7 +1588,7 @@ export class OrderService {
             ctx,
             order,
             promotions,
-            updatedOrderLine ? [updatedOrderLine] : [],
+            updatedOrderLines ?? [],
         );
         const updateFields: Array<keyof OrderItem> = [
             'initialListPrice',

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

@@ -211,7 +211,8 @@ export class ProductVariantService {
             })
             .leftJoin('productvariant.collections', 'collection')
             .leftJoin('productvariant.product', 'product')
-            .andWhere('product.deletedAt IS NULL', { deletedAt: null })
+            .andWhere('product.deletedAt IS NULL')
+            .andWhere('productvariant.deletedAt IS NULL')
             .andWhere('collection.id = :collectionId', { collectionId });
 
         if (options && options.filter && options.filter.enabled && options.filter.enabled.eq === true) {

+ 2 - 2
packages/core/src/service/services/product.service.ts

@@ -250,7 +250,7 @@ export class ProductService {
      * Assigns a Product to the specified Channel, and optionally uses a `priceFactor` to set the ProductVariantPrices
      * on the new Channel.
      *
-     * Internally, this method will also call {@link ProductVariantService} `assignassignProductVariantsToChannel()` for
+     * Internally, this method will also call {@link ProductVariantService} `assignProductVariantsToChannel()` for
      * each of the Product's variants, and will assign the Product's Assets to the Channel too.
      */
     async assignProductsToChannel(
@@ -270,7 +270,7 @@ export class ProductService {
             priceFactor: input.priceFactor,
         });
         const assetIds: ID[] = unique(
-            ([] as ID[]).concat(...productsWithVariants.map(p => p.assets.map(a => a.id))),
+            ([] as ID[]).concat(...productsWithVariants.map(p => p.assets.map(a => a.assetId))),
         );
         await this.assetService.assignToChannel(ctx, { channelId: input.channelId, assetIds });
         const products = await this.connection.getRepository(ctx, Product).findByIds(input.productIds);

+ 3 - 3
packages/create/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/create",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "license": "MIT",
   "bin": {
     "create": "./index.js"
@@ -28,13 +28,13 @@
     "@types/handlebars": "^4.1.0",
     "@types/listr": "^0.14.2",
     "@types/semver": "^6.2.2",
-    "@vendure/core": "^1.3.3",
+    "@vendure/core": "^1.3.4",
     "rimraf": "^3.0.2",
     "ts-node": "^10.2.1",
     "typescript": "4.3.5"
   },
   "dependencies": {
-    "@vendure/common": "^1.3.3",
+    "@vendure/common": "^1.3.4",
     "chalk": "^4.1.0",
     "commander": "^7.1.0",
     "cross-spawn": "^7.0.3",

+ 9 - 9
packages/dev-server/package.json

@@ -1,6 +1,6 @@
 {
   "name": "dev-server",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "main": "index.js",
   "license": "MIT",
   "private": true,
@@ -14,18 +14,18 @@
     "load-test:100k": "node -r ts-node/register load-testing/run-load-test.ts 100000"
   },
   "dependencies": {
-    "@vendure/admin-ui-plugin": "^1.3.3",
-    "@vendure/asset-server-plugin": "^1.3.3",
-    "@vendure/common": "^1.3.3",
-    "@vendure/core": "^1.3.3",
-    "@vendure/elasticsearch-plugin": "^1.3.3",
-    "@vendure/email-plugin": "^1.3.3",
+    "@vendure/admin-ui-plugin": "^1.3.4",
+    "@vendure/asset-server-plugin": "^1.3.4",
+    "@vendure/common": "^1.3.4",
+    "@vendure/core": "^1.3.4",
+    "@vendure/elasticsearch-plugin": "^1.3.4",
+    "@vendure/email-plugin": "^1.3.4",
     "typescript": "4.3.5"
   },
   "devDependencies": {
     "@types/csv-stringify": "^3.1.0",
-    "@vendure/testing": "^1.3.3",
-    "@vendure/ui-devkit": "^1.3.3",
+    "@vendure/testing": "^1.3.4",
+    "@vendure/ui-devkit": "^1.3.4",
     "concurrently": "^5.0.0",
     "csv-stringify": "^5.3.3"
   }

+ 3 - 3
packages/elasticsearch-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/elasticsearch-plugin",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "lib/index.d.ts",
@@ -25,8 +25,8 @@
     "fast-deep-equal": "^3.1.3"
   },
   "devDependencies": {
-    "@vendure/common": "^1.3.3",
-    "@vendure/core": "^1.3.3",
+    "@vendure/common": "^1.3.4",
+    "@vendure/core": "^1.3.4",
     "rimraf": "^3.0.2",
     "typescript": "4.3.5"
   }

+ 3 - 3
packages/email-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/email-plugin",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "license": "MIT",
   "main": "lib/index.js",
   "types": "lib/index.d.ts",
@@ -35,8 +35,8 @@
     "@types/fs-extra": "^9.0.1",
     "@types/handlebars": "^4.1.0",
     "@types/mjml": "^4.0.4",
-    "@vendure/common": "^1.3.3",
-    "@vendure/core": "^1.3.3",
+    "@vendure/common": "^1.3.4",
+    "@vendure/core": "^1.3.4",
     "rimraf": "^3.0.2",
     "typescript": "4.3.5"
   }

+ 3 - 3
packages/job-queue-plugin/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/job-queue-plugin",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "license": "MIT",
   "main": "package/index.js",
   "types": "package/index.d.ts",
@@ -24,8 +24,8 @@
   "devDependencies": {
     "@google-cloud/pubsub": "^2.8.0",
     "@types/redis": "^2.8.28",
-    "@vendure/common": "^1.3.3",
-    "@vendure/core": "^1.3.3",
+    "@vendure/common": "^1.3.4",
+    "@vendure/core": "^1.3.4",
     "bullmq": "^1.40.1",
     "redis": "^3.0.2",
     "rimraf": "^3.0.2",

+ 4 - 4
packages/payment-plugin/package.json

@@ -1,6 +1,6 @@
 {
     "name": "@vendure/payments-plugin",
-    "version": "1.3.3",
+    "version": "1.3.4",
     "license": "MIT",
     "main": "package/index.js",
     "types": "package/index.d.ts",
@@ -26,9 +26,9 @@
     },
     "devDependencies": {
         "@types/braintree": "^2.22.15",
-        "@vendure/common": "^1.3.2",
-        "@vendure/core": "^1.3.2",
-        "@vendure/testing": "^1.3.2",
+        "@vendure/common": "^1.3.4",
+        "@vendure/core": "^1.3.4",
+        "@vendure/testing": "^1.3.4",
         "braintree": "^3.0.0",
         "@mollie/api-client": "^3.5.1",
         "nock": "^13.1.4",

+ 3 - 3
packages/testing/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/testing",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "description": "End-to-end testing tools for Vendure projects",
   "keywords": [
     "vendure",
@@ -34,7 +34,7 @@
   },
   "dependencies": {
     "@types/node-fetch": "^2.5.4",
-    "@vendure/common": "^1.3.3",
+    "@vendure/common": "^1.3.4",
     "faker": "^4.1.0",
     "form-data": "^3.0.0",
     "graphql": "15.5.1",
@@ -45,7 +45,7 @@
   "devDependencies": {
     "@types/mysql": "^2.15.15",
     "@types/pg": "^7.14.5",
-    "@vendure/core": "^1.3.3",
+    "@vendure/core": "^1.3.4",
     "mysql": "^2.18.1",
     "pg": "^8.4.0",
     "rimraf": "^3.0.0",

+ 1 - 1
packages/testing/src/error-result-guard.ts

@@ -1,4 +1,4 @@
-declare function fail(error?: any): never;
+import { fail } from 'assert';
 
 /**
  * @description

+ 4 - 4
packages/ui-devkit/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@vendure/ui-devkit",
-  "version": "1.3.3",
+  "version": "1.3.4",
   "description": "A library for authoring Vendure Admin UI extensions",
   "keywords": [
     "vendure",
@@ -40,8 +40,8 @@
     "@angular/cli": "12.2.2",
     "@angular/compiler": "12.2.2",
     "@angular/compiler-cli": "12.2.2",
-    "@vendure/admin-ui": "^1.3.3",
-    "@vendure/common": "^1.3.3",
+    "@vendure/admin-ui": "^1.3.4",
+    "@vendure/common": "^1.3.4",
     "chalk": "^4.1.0",
     "chokidar": "^3.5.1",
     "fs-extra": "^10.0.0",
@@ -52,7 +52,7 @@
     "@rollup/plugin-node-resolve": "^11.2.0",
     "@types/fs-extra": "^9.0.8",
     "@types/glob": "^7.1.3",
-    "@vendure/core": "^1.3.3",
+    "@vendure/core": "^1.3.4",
     "rimraf": "^3.0.2",
     "rollup": "^2.40.0",
     "rollup-plugin-terser": "^7.0.2",