Browse Source

feat(core): Add Facet/Collection Channel assignment mutations

Artem Danilov 3 years ago
parent
commit
34840c943d

+ 66 - 1
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -233,6 +233,16 @@ export type AssignAssetsToChannelInput = {
   channelId: Scalars['ID'];
 };
 
+export type AssignCollectionsToChannelInput = {
+  collectionIds: Array<Scalars['ID']>;
+  channelId: Scalars['ID'];
+};
+
+export type AssignFacetsToChannelInput = {
+  facetIds: Array<Scalars['ID']>;
+  channelId: Scalars['ID'];
+};
+
 export type AssignProductVariantsToChannelInput = {
   productVariantIds: Array<Scalars['ID']>;
   channelId: Scalars['ID'];
@@ -1424,6 +1434,7 @@ export enum ErrorCode {
   UNKNOWN_ERROR = 'UNKNOWN_ERROR',
   MIME_TYPE_ERROR = 'MIME_TYPE_ERROR',
   LANGUAGE_NOT_AVAILABLE_ERROR = 'LANGUAGE_NOT_AVAILABLE_ERROR',
+  FACET_IN_USE_ERROR = 'FACET_IN_USE_ERROR',
   CHANNEL_DEFAULT_LANGUAGE_ERROR = 'CHANNEL_DEFAULT_LANGUAGE_ERROR',
   SETTLE_PAYMENT_ERROR = 'SETTLE_PAYMENT_ERROR',
   CANCEL_PAYMENT_ERROR = 'CANCEL_PAYMENT_ERROR',
@@ -1490,6 +1501,14 @@ export type FacetFilterParameter = {
   code?: Maybe<StringOperators>;
 };
 
+export type FacetInUseError = ErrorResult & {
+  __typename?: 'FacetInUseError';
+  errorCode: ErrorCode;
+  message: Scalars['String'];
+  productCount: Scalars['Int'];
+  variantCount: Scalars['Int'];
+};
+
 export type FacetList = PaginatedList & {
   __typename?: 'FacetList';
   items: Array<Facet>;
@@ -2345,6 +2364,10 @@ export type Mutation = {
   addOptionGroupToProduct: Product;
   /** Assign assets to channel */
   assignAssetsToChannel: Array<Asset>;
+  /** Assigns Collections to the specified Channel */
+  assignCollectionsToChannel: Array<Collection>;
+  /** Assigns Facets to the specified Channel */
+  assignFacetsToChannel: Array<Facet>;
   /** Assigns ProductVariants to the specified Channel */
   assignProductVariantsToChannel: Array<ProductVariant>;
   /** Assigns all ProductVariants of Product to the specified Channel */
@@ -2460,8 +2483,12 @@ export type Mutation = {
   moveCollection: Collection;
   refundOrder: RefundOrderResult;
   reindex: Job;
+  /** Removes Collections from the specified Channel */
+  removeCollectionsFromChannel: Array<Collection>;
   /** Remove Customers from a CustomerGroup */
   removeCustomersFromGroup: CustomerGroup;
+  /** Removes Facets from the specified Channel */
+  removeFacetsFromChannel: Array<RemoveFacetFromChannelResult>;
   /** Remove members from a Zone */
   removeMembersFromZone: Zone;
   /** Remove an OptionGroup from a Product */
@@ -2586,6 +2613,16 @@ export type MutationAssignAssetsToChannelArgs = {
 };
 
 
+export type MutationAssignCollectionsToChannelArgs = {
+  input: AssignCollectionsToChannelInput;
+};
+
+
+export type MutationAssignFacetsToChannelArgs = {
+  input: AssignFacetsToChannelInput;
+};
+
+
 export type MutationAssignProductVariantsToChannelArgs = {
   input: AssignProductVariantsToChannelInput;
 };
@@ -2895,12 +2932,22 @@ export type MutationRefundOrderArgs = {
 };
 
 
+export type MutationRemoveCollectionsFromChannelArgs = {
+  input: RemoveCollectionsFromChannelInput;
+};
+
+
 export type MutationRemoveCustomersFromGroupArgs = {
   customerGroupId: Scalars['ID'];
   customerIds: Array<Scalars['ID']>;
 };
 
 
+export type MutationRemoveFacetsFromChannelArgs = {
+  input: RemoveFacetsFromChannelInput;
+};
+
+
 export type MutationRemoveMembersFromZoneArgs = {
   zoneId: Scalars['ID'];
   memberIds: Array<Scalars['ID']>;
@@ -4551,6 +4598,19 @@ export type Release = Node & StockMovement & {
   orderItem: OrderItem;
 };
 
+export type RemoveCollectionsFromChannelInput = {
+  collectionIds: Array<Scalars['ID']>;
+  channelId: Scalars['ID'];
+};
+
+export type RemoveFacetFromChannelResult = Facet | FacetInUseError;
+
+export type RemoveFacetsFromChannelInput = {
+  facetIds: Array<Scalars['ID']>;
+  channelId: Scalars['ID'];
+  force?: Maybe<Scalars['Boolean']>;
+};
+
 export type RemoveOptionGroupFromProductResult = Product | ProductOptionInUseError;
 
 export type RemoveProductVariantsFromChannelInput = {
@@ -8985,6 +9045,11 @@ type ErrorResult_EmptyOrderLineSelectionError_Fragment = (
   & Pick<EmptyOrderLineSelectionError, 'errorCode' | 'message'>
 );
 
+type ErrorResult_FacetInUseError_Fragment = (
+  { __typename?: 'FacetInUseError' }
+  & Pick<FacetInUseError, 'errorCode' | 'message'>
+);
+
 type ErrorResult_FulfillmentStateTransitionError_Fragment = (
   { __typename?: 'FulfillmentStateTransitionError' }
   & Pick<FulfillmentStateTransitionError, 'errorCode' | 'message'>
@@ -9120,7 +9185,7 @@ type ErrorResult_SettlePaymentError_Fragment = (
   & Pick<SettlePaymentError, 'errorCode' | 'message'>
 );
 
-export type ErrorResultFragment = ErrorResult_AlreadyRefundedError_Fragment | ErrorResult_CancelActiveOrderError_Fragment | ErrorResult_CancelPaymentError_Fragment | ErrorResult_ChannelDefaultLanguageError_Fragment | ErrorResult_CouponCodeExpiredError_Fragment | ErrorResult_CouponCodeInvalidError_Fragment | ErrorResult_CouponCodeLimitError_Fragment | ErrorResult_CreateFulfillmentError_Fragment | ErrorResult_EmailAddressConflictError_Fragment | ErrorResult_EmptyOrderLineSelectionError_Fragment | ErrorResult_FulfillmentStateTransitionError_Fragment | ErrorResult_InsufficientStockError_Fragment | ErrorResult_InsufficientStockOnHandError_Fragment | ErrorResult_InvalidCredentialsError_Fragment | ErrorResult_InvalidFulfillmentHandlerError_Fragment | ErrorResult_ItemsAlreadyFulfilledError_Fragment | ErrorResult_LanguageNotAvailableError_Fragment | ErrorResult_ManualPaymentStateError_Fragment | ErrorResult_MimeTypeError_Fragment | ErrorResult_MissingConditionsError_Fragment | ErrorResult_MultipleOrderError_Fragment | ErrorResult_NativeAuthStrategyError_Fragment | ErrorResult_NegativeQuantityError_Fragment | ErrorResult_NoChangesSpecifiedError_Fragment | ErrorResult_NothingToRefundError_Fragment | ErrorResult_OrderLimitError_Fragment | ErrorResult_OrderModificationStateError_Fragment | ErrorResult_OrderStateTransitionError_Fragment | ErrorResult_PaymentMethodMissingError_Fragment | ErrorResult_PaymentOrderMismatchError_Fragment | ErrorResult_PaymentStateTransitionError_Fragment | ErrorResult_ProductOptionInUseError_Fragment | ErrorResult_QuantityTooGreatError_Fragment | ErrorResult_RefundOrderStateError_Fragment | ErrorResult_RefundPaymentIdMissingError_Fragment | ErrorResult_RefundStateTransitionError_Fragment | ErrorResult_SettlePaymentError_Fragment;
+export type ErrorResultFragment = ErrorResult_AlreadyRefundedError_Fragment | ErrorResult_CancelActiveOrderError_Fragment | ErrorResult_CancelPaymentError_Fragment | ErrorResult_ChannelDefaultLanguageError_Fragment | ErrorResult_CouponCodeExpiredError_Fragment | ErrorResult_CouponCodeInvalidError_Fragment | ErrorResult_CouponCodeLimitError_Fragment | ErrorResult_CreateFulfillmentError_Fragment | ErrorResult_EmailAddressConflictError_Fragment | ErrorResult_EmptyOrderLineSelectionError_Fragment | ErrorResult_FacetInUseError_Fragment | ErrorResult_FulfillmentStateTransitionError_Fragment | ErrorResult_InsufficientStockError_Fragment | ErrorResult_InsufficientStockOnHandError_Fragment | ErrorResult_InvalidCredentialsError_Fragment | ErrorResult_InvalidFulfillmentHandlerError_Fragment | ErrorResult_ItemsAlreadyFulfilledError_Fragment | ErrorResult_LanguageNotAvailableError_Fragment | ErrorResult_ManualPaymentStateError_Fragment | ErrorResult_MimeTypeError_Fragment | ErrorResult_MissingConditionsError_Fragment | ErrorResult_MultipleOrderError_Fragment | ErrorResult_NativeAuthStrategyError_Fragment | ErrorResult_NegativeQuantityError_Fragment | ErrorResult_NoChangesSpecifiedError_Fragment | ErrorResult_NothingToRefundError_Fragment | ErrorResult_OrderLimitError_Fragment | ErrorResult_OrderModificationStateError_Fragment | ErrorResult_OrderStateTransitionError_Fragment | ErrorResult_PaymentMethodMissingError_Fragment | ErrorResult_PaymentOrderMismatchError_Fragment | ErrorResult_PaymentStateTransitionError_Fragment | ErrorResult_ProductOptionInUseError_Fragment | ErrorResult_QuantityTooGreatError_Fragment | ErrorResult_RefundOrderStateError_Fragment | ErrorResult_RefundPaymentIdMissingError_Fragment | ErrorResult_RefundStateTransitionError_Fragment | ErrorResult_SettlePaymentError_Fragment;
 
 export type ShippingMethodFragment = (
   { __typename?: 'ShippingMethod' }

+ 273 - 197
packages/admin-ui/src/lib/core/src/common/introspection-result.ts

@@ -1,200 +1,276 @@
 // tslint:disable
 
-export interface PossibleTypesResultData {
-    possibleTypes: {
-        [key: string]: string[];
-    };
-}
-const result: PossibleTypesResultData = {
-    possibleTypes: {
-        AddFulfillmentToOrderResult: [
-            'Fulfillment',
-            'EmptyOrderLineSelectionError',
-            'ItemsAlreadyFulfilledError',
-            'InsufficientStockOnHandError',
-            'InvalidFulfillmentHandlerError',
-            'FulfillmentStateTransitionError',
-            'CreateFulfillmentError',
-        ],
-        AddManualPaymentToOrderResult: ['Order', 'ManualPaymentStateError'],
-        AuthenticationResult: ['CurrentUser', 'InvalidCredentialsError'],
-        CancelOrderResult: [
-            'Order',
-            'EmptyOrderLineSelectionError',
-            'QuantityTooGreatError',
-            'MultipleOrderError',
-            'CancelActiveOrderError',
-            'OrderStateTransitionError',
-        ],
-        CancelPaymentResult: ['Payment', 'CancelPaymentError', 'PaymentStateTransitionError'],
-        CreateAssetResult: ['Asset', 'MimeTypeError'],
-        CreateChannelResult: ['Channel', 'LanguageNotAvailableError'],
-        CreateCustomerResult: ['Customer', 'EmailAddressConflictError'],
-        CreatePromotionResult: ['Promotion', 'MissingConditionsError'],
-        CustomField: [
-            'BooleanCustomFieldConfig',
-            'DateTimeCustomFieldConfig',
-            'FloatCustomFieldConfig',
-            'IntCustomFieldConfig',
-            'LocaleStringCustomFieldConfig',
-            'RelationCustomFieldConfig',
-            'StringCustomFieldConfig',
-            'TextCustomFieldConfig',
-        ],
-        CustomFieldConfig: [
-            'StringCustomFieldConfig',
-            'LocaleStringCustomFieldConfig',
-            'IntCustomFieldConfig',
-            'FloatCustomFieldConfig',
-            'BooleanCustomFieldConfig',
-            'DateTimeCustomFieldConfig',
-            'RelationCustomFieldConfig',
-            'TextCustomFieldConfig',
-        ],
-        ErrorResult: [
-            'AlreadyRefundedError',
-            'CancelActiveOrderError',
-            'CancelPaymentError',
-            'ChannelDefaultLanguageError',
-            'CouponCodeExpiredError',
-            'CouponCodeInvalidError',
-            'CouponCodeLimitError',
-            'CreateFulfillmentError',
-            'EmailAddressConflictError',
-            'EmptyOrderLineSelectionError',
-            'FulfillmentStateTransitionError',
-            'InsufficientStockError',
-            'InsufficientStockOnHandError',
-            'InvalidCredentialsError',
-            'InvalidFulfillmentHandlerError',
-            'ItemsAlreadyFulfilledError',
-            'LanguageNotAvailableError',
-            'ManualPaymentStateError',
-            'MimeTypeError',
-            'MissingConditionsError',
-            'MultipleOrderError',
-            'NativeAuthStrategyError',
-            'NegativeQuantityError',
-            'NoChangesSpecifiedError',
-            'NothingToRefundError',
-            'OrderLimitError',
-            'OrderModificationStateError',
-            'OrderStateTransitionError',
-            'PaymentMethodMissingError',
-            'PaymentOrderMismatchError',
-            'PaymentStateTransitionError',
-            'ProductOptionInUseError',
-            'QuantityTooGreatError',
-            'RefundOrderStateError',
-            'RefundPaymentIdMissingError',
-            'RefundStateTransitionError',
-            'SettlePaymentError',
-        ],
-        ModifyOrderResult: [
-            'Order',
-            'NoChangesSpecifiedError',
-            'OrderModificationStateError',
-            'PaymentMethodMissingError',
-            'RefundPaymentIdMissingError',
-            'OrderLimitError',
-            'NegativeQuantityError',
-            'InsufficientStockError',
-            'CouponCodeExpiredError',
-            'CouponCodeInvalidError',
-            'CouponCodeLimitError',
-        ],
-        NativeAuthenticationResult: ['CurrentUser', 'InvalidCredentialsError', 'NativeAuthStrategyError'],
-        Node: [
-            'Address',
-            'Administrator',
-            'Allocation',
-            'Asset',
-            'AuthenticationMethod',
-            'Cancellation',
-            'Channel',
-            'Collection',
-            'Country',
-            'Customer',
-            'CustomerGroup',
-            'Facet',
-            'FacetValue',
-            'Fulfillment',
-            'HistoryEntry',
-            'Job',
-            'Order',
-            'OrderItem',
-            'OrderLine',
-            'OrderModification',
-            'Payment',
-            'PaymentMethod',
-            'Product',
-            'ProductOption',
-            'ProductOptionGroup',
-            'ProductVariant',
-            'Promotion',
-            'Refund',
-            'Release',
-            'Return',
-            'Role',
-            'Sale',
-            'ShippingMethod',
-            'StockAdjustment',
-            'Surcharge',
-            'Tag',
-            'TaxCategory',
-            'TaxRate',
-            'User',
-            'Zone',
-        ],
-        PaginatedList: [
-            'AdministratorList',
-            'AssetList',
-            'CollectionList',
-            'CountryList',
-            'CustomerGroupList',
-            'CustomerList',
-            'FacetList',
-            'HistoryEntryList',
-            'JobList',
-            'OrderList',
-            'PaymentMethodList',
-            'ProductList',
-            'ProductVariantList',
-            'PromotionList',
-            'RoleList',
-            'ShippingMethodList',
-            'TagList',
-            'TaxRateList',
-        ],
-        RefundOrderResult: [
-            'Refund',
-            'QuantityTooGreatError',
-            'NothingToRefundError',
-            'OrderStateTransitionError',
-            'MultipleOrderError',
-            'PaymentOrderMismatchError',
-            'RefundOrderStateError',
-            'AlreadyRefundedError',
-            'RefundStateTransitionError',
-        ],
-        RemoveOptionGroupFromProductResult: ['Product', 'ProductOptionInUseError'],
-        SearchResultPrice: ['PriceRange', 'SinglePrice'],
-        SettlePaymentResult: [
-            'Payment',
-            'SettlePaymentError',
-            'PaymentStateTransitionError',
-            'OrderStateTransitionError',
-        ],
-        SettleRefundResult: ['Refund', 'RefundStateTransitionError'],
-        StockMovement: ['Allocation', 'Cancellation', 'Release', 'Return', 'Sale', 'StockAdjustment'],
-        StockMovementItem: ['StockAdjustment', 'Allocation', 'Sale', 'Cancellation', 'Return', 'Release'],
-        TransitionFulfillmentToStateResult: ['Fulfillment', 'FulfillmentStateTransitionError'],
-        TransitionOrderToStateResult: ['Order', 'OrderStateTransitionError'],
-        TransitionPaymentToStateResult: ['Payment', 'PaymentStateTransitionError'],
-        UpdateChannelResult: ['Channel', 'LanguageNotAvailableError'],
-        UpdateCustomerResult: ['Customer', 'EmailAddressConflictError'],
-        UpdateGlobalSettingsResult: ['GlobalSettings', 'ChannelDefaultLanguageError'],
-        UpdatePromotionResult: ['Promotion', 'MissingConditionsError'],
-    },
+      export interface PossibleTypesResultData {
+        possibleTypes: {
+          [key: string]: string[]
+        }
+      }
+      const result: PossibleTypesResultData = {
+  "possibleTypes": {
+    "AddFulfillmentToOrderResult": [
+      "Fulfillment",
+      "EmptyOrderLineSelectionError",
+      "ItemsAlreadyFulfilledError",
+      "InsufficientStockOnHandError",
+      "InvalidFulfillmentHandlerError",
+      "FulfillmentStateTransitionError",
+      "CreateFulfillmentError"
+    ],
+    "AddManualPaymentToOrderResult": [
+      "Order",
+      "ManualPaymentStateError"
+    ],
+    "AuthenticationResult": [
+      "CurrentUser",
+      "InvalidCredentialsError"
+    ],
+    "CancelOrderResult": [
+      "Order",
+      "EmptyOrderLineSelectionError",
+      "QuantityTooGreatError",
+      "MultipleOrderError",
+      "CancelActiveOrderError",
+      "OrderStateTransitionError"
+    ],
+    "CancelPaymentResult": [
+      "Payment",
+      "CancelPaymentError",
+      "PaymentStateTransitionError"
+    ],
+    "CreateAssetResult": [
+      "Asset",
+      "MimeTypeError"
+    ],
+    "CreateChannelResult": [
+      "Channel",
+      "LanguageNotAvailableError"
+    ],
+    "CreateCustomerResult": [
+      "Customer",
+      "EmailAddressConflictError"
+    ],
+    "CreatePromotionResult": [
+      "Promotion",
+      "MissingConditionsError"
+    ],
+    "CustomField": [
+      "BooleanCustomFieldConfig",
+      "DateTimeCustomFieldConfig",
+      "FloatCustomFieldConfig",
+      "IntCustomFieldConfig",
+      "LocaleStringCustomFieldConfig",
+      "RelationCustomFieldConfig",
+      "StringCustomFieldConfig",
+      "TextCustomFieldConfig"
+    ],
+    "CustomFieldConfig": [
+      "StringCustomFieldConfig",
+      "LocaleStringCustomFieldConfig",
+      "IntCustomFieldConfig",
+      "FloatCustomFieldConfig",
+      "BooleanCustomFieldConfig",
+      "DateTimeCustomFieldConfig",
+      "RelationCustomFieldConfig",
+      "TextCustomFieldConfig"
+    ],
+    "ErrorResult": [
+      "AlreadyRefundedError",
+      "CancelActiveOrderError",
+      "CancelPaymentError",
+      "ChannelDefaultLanguageError",
+      "CouponCodeExpiredError",
+      "CouponCodeInvalidError",
+      "CouponCodeLimitError",
+      "CreateFulfillmentError",
+      "EmailAddressConflictError",
+      "EmptyOrderLineSelectionError",
+      "FacetInUseError",
+      "FulfillmentStateTransitionError",
+      "InsufficientStockError",
+      "InsufficientStockOnHandError",
+      "InvalidCredentialsError",
+      "InvalidFulfillmentHandlerError",
+      "ItemsAlreadyFulfilledError",
+      "LanguageNotAvailableError",
+      "ManualPaymentStateError",
+      "MimeTypeError",
+      "MissingConditionsError",
+      "MultipleOrderError",
+      "NativeAuthStrategyError",
+      "NegativeQuantityError",
+      "NoChangesSpecifiedError",
+      "NothingToRefundError",
+      "OrderLimitError",
+      "OrderModificationStateError",
+      "OrderStateTransitionError",
+      "PaymentMethodMissingError",
+      "PaymentOrderMismatchError",
+      "PaymentStateTransitionError",
+      "ProductOptionInUseError",
+      "QuantityTooGreatError",
+      "RefundOrderStateError",
+      "RefundPaymentIdMissingError",
+      "RefundStateTransitionError",
+      "SettlePaymentError"
+    ],
+    "ModifyOrderResult": [
+      "Order",
+      "NoChangesSpecifiedError",
+      "OrderModificationStateError",
+      "PaymentMethodMissingError",
+      "RefundPaymentIdMissingError",
+      "OrderLimitError",
+      "NegativeQuantityError",
+      "InsufficientStockError",
+      "CouponCodeExpiredError",
+      "CouponCodeInvalidError",
+      "CouponCodeLimitError"
+    ],
+    "NativeAuthenticationResult": [
+      "CurrentUser",
+      "InvalidCredentialsError",
+      "NativeAuthStrategyError"
+    ],
+    "Node": [
+      "Address",
+      "Administrator",
+      "Allocation",
+      "Asset",
+      "AuthenticationMethod",
+      "Cancellation",
+      "Channel",
+      "Collection",
+      "Country",
+      "Customer",
+      "CustomerGroup",
+      "Facet",
+      "FacetValue",
+      "Fulfillment",
+      "HistoryEntry",
+      "Job",
+      "Order",
+      "OrderItem",
+      "OrderLine",
+      "OrderModification",
+      "Payment",
+      "PaymentMethod",
+      "Product",
+      "ProductOption",
+      "ProductOptionGroup",
+      "ProductVariant",
+      "Promotion",
+      "Refund",
+      "Release",
+      "Return",
+      "Role",
+      "Sale",
+      "ShippingMethod",
+      "StockAdjustment",
+      "Surcharge",
+      "Tag",
+      "TaxCategory",
+      "TaxRate",
+      "User",
+      "Zone"
+    ],
+    "PaginatedList": [
+      "AdministratorList",
+      "AssetList",
+      "CollectionList",
+      "CountryList",
+      "CustomerGroupList",
+      "CustomerList",
+      "FacetList",
+      "HistoryEntryList",
+      "JobList",
+      "OrderList",
+      "PaymentMethodList",
+      "ProductList",
+      "ProductVariantList",
+      "PromotionList",
+      "RoleList",
+      "ShippingMethodList",
+      "TagList",
+      "TaxRateList"
+    ],
+    "RefundOrderResult": [
+      "Refund",
+      "QuantityTooGreatError",
+      "NothingToRefundError",
+      "OrderStateTransitionError",
+      "MultipleOrderError",
+      "PaymentOrderMismatchError",
+      "RefundOrderStateError",
+      "AlreadyRefundedError",
+      "RefundStateTransitionError"
+    ],
+    "RemoveFacetFromChannelResult": [
+      "Facet",
+      "FacetInUseError"
+    ],
+    "RemoveOptionGroupFromProductResult": [
+      "Product",
+      "ProductOptionInUseError"
+    ],
+    "SearchResultPrice": [
+      "PriceRange",
+      "SinglePrice"
+    ],
+    "SettlePaymentResult": [
+      "Payment",
+      "SettlePaymentError",
+      "PaymentStateTransitionError",
+      "OrderStateTransitionError"
+    ],
+    "SettleRefundResult": [
+      "Refund",
+      "RefundStateTransitionError"
+    ],
+    "StockMovement": [
+      "Allocation",
+      "Cancellation",
+      "Release",
+      "Return",
+      "Sale",
+      "StockAdjustment"
+    ],
+    "StockMovementItem": [
+      "StockAdjustment",
+      "Allocation",
+      "Sale",
+      "Cancellation",
+      "Return",
+      "Release"
+    ],
+    "TransitionFulfillmentToStateResult": [
+      "Fulfillment",
+      "FulfillmentStateTransitionError"
+    ],
+    "TransitionOrderToStateResult": [
+      "Order",
+      "OrderStateTransitionError"
+    ],
+    "TransitionPaymentToStateResult": [
+      "Payment",
+      "PaymentStateTransitionError"
+    ],
+    "UpdateChannelResult": [
+      "Channel",
+      "LanguageNotAvailableError"
+    ],
+    "UpdateCustomerResult": [
+      "Customer",
+      "EmailAddressConflictError"
+    ],
+    "UpdateGlobalSettingsResult": [
+      "GlobalSettings",
+      "ChannelDefaultLanguageError"
+    ],
+    "UpdatePromotionResult": [
+      "Promotion",
+      "MissingConditionsError"
+    ]
+  }
 };
-export default result;
+      export default result;
+    

File diff suppressed because it is too large
+ 501 - 505
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts


File diff suppressed because it is too large
+ 736 - 751
packages/common/src/generated-shop-types.ts


+ 60 - 0
packages/common/src/generated-types.ts

@@ -232,6 +232,16 @@ export type AssignAssetsToChannelInput = {
   channelId: Scalars['ID'];
 };
 
+export type AssignCollectionsToChannelInput = {
+  collectionIds: Array<Scalars['ID']>;
+  channelId: Scalars['ID'];
+};
+
+export type AssignFacetsToChannelInput = {
+  facetIds: Array<Scalars['ID']>;
+  channelId: Scalars['ID'];
+};
+
 export type AssignProductVariantsToChannelInput = {
   productVariantIds: Array<Scalars['ID']>;
   channelId: Scalars['ID'];
@@ -1416,6 +1426,7 @@ export enum ErrorCode {
   UNKNOWN_ERROR = 'UNKNOWN_ERROR',
   MIME_TYPE_ERROR = 'MIME_TYPE_ERROR',
   LANGUAGE_NOT_AVAILABLE_ERROR = 'LANGUAGE_NOT_AVAILABLE_ERROR',
+  FACET_IN_USE_ERROR = 'FACET_IN_USE_ERROR',
   CHANNEL_DEFAULT_LANGUAGE_ERROR = 'CHANNEL_DEFAULT_LANGUAGE_ERROR',
   SETTLE_PAYMENT_ERROR = 'SETTLE_PAYMENT_ERROR',
   CANCEL_PAYMENT_ERROR = 'CANCEL_PAYMENT_ERROR',
@@ -1482,6 +1493,14 @@ export type FacetFilterParameter = {
   code?: Maybe<StringOperators>;
 };
 
+export type FacetInUseError = ErrorResult & {
+  __typename?: 'FacetInUseError';
+  errorCode: ErrorCode;
+  message: Scalars['String'];
+  productCount: Scalars['Int'];
+  variantCount: Scalars['Int'];
+};
+
 export type FacetList = PaginatedList & {
   __typename?: 'FacetList';
   items: Array<Facet>;
@@ -2355,6 +2374,10 @@ export type Mutation = {
   deleteCollection: DeletionResponse;
   /** Move a Collection to a different parent or index */
   moveCollection: Collection;
+  /** Assigns Collections to the specified Channel */
+  assignCollectionsToChannel: Array<Collection>;
+  /** Removes Collections from the specified Channel */
+  removeCollectionsFromChannel: Array<Collection>;
   /** Create a new Country */
   createCountry: Country;
   /** Update an existing Country */
@@ -2398,6 +2421,10 @@ export type Mutation = {
   updateFacetValues: Array<FacetValue>;
   /** Delete one or more FacetValues */
   deleteFacetValues: Array<DeletionResponse>;
+  /** Assigns Facets to the specified Channel */
+  assignFacetsToChannel: Array<Facet>;
+  /** Removes Facets from the specified Channel */
+  removeFacetsFromChannel: Array<RemoveFacetFromChannelResult>;
   updateGlobalSettings: UpdateGlobalSettingsResult;
   importProducts?: Maybe<ImportInfo>;
   /** Remove all settled jobs in the given queues older than the given date. Returns the number of jobs deleted. */
@@ -2623,6 +2650,16 @@ export type MutationMoveCollectionArgs = {
 };
 
 
+export type MutationAssignCollectionsToChannelArgs = {
+  input: AssignCollectionsToChannelInput;
+};
+
+
+export type MutationRemoveCollectionsFromChannelArgs = {
+  input: RemoveCollectionsFromChannelInput;
+};
+
+
 export type MutationCreateCountryArgs = {
   input: CreateCountryInput;
 };
@@ -2744,6 +2781,16 @@ export type MutationDeleteFacetValuesArgs = {
 };
 
 
+export type MutationAssignFacetsToChannelArgs = {
+  input: AssignFacetsToChannelInput;
+};
+
+
+export type MutationRemoveFacetsFromChannelArgs = {
+  input: RemoveFacetsFromChannelInput;
+};
+
+
 export type MutationUpdateGlobalSettingsArgs = {
   input: UpdateGlobalSettingsInput;
 };
@@ -4483,6 +4530,19 @@ export type Release = Node & StockMovement & {
   orderItem: OrderItem;
 };
 
+export type RemoveCollectionsFromChannelInput = {
+  collectionIds: Array<Scalars['ID']>;
+  channelId: Scalars['ID'];
+};
+
+export type RemoveFacetFromChannelResult = Facet | FacetInUseError;
+
+export type RemoveFacetsFromChannelInput = {
+  facetIds: Array<Scalars['ID']>;
+  channelId: Scalars['ID'];
+  force?: Maybe<Scalars['Boolean']>;
+};
+
 export type RemoveOptionGroupFromProductResult = Product | ProductOptionInUseError;
 
 export type RemoveProductVariantsFromChannelInput = {

File diff suppressed because it is too large
+ 501 - 505
packages/core/e2e/graphql/generated-e2e-admin-types.ts


File diff suppressed because it is too large
+ 699 - 714
packages/core/e2e/graphql/generated-e2e-shop-types.ts


+ 22 - 2
packages/core/src/api/resolvers/admin/collection.resolver.ts

@@ -1,10 +1,10 @@
 import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 import {
     ConfigurableOperationDefinition,
-    DeletionResponse,
+    DeletionResponse, MutationAssignCollectionsToChannelArgs,
     MutationCreateCollectionArgs,
     MutationDeleteCollectionArgs,
-    MutationMoveCollectionArgs,
+    MutationMoveCollectionArgs, MutationRemoveCollectionsFromChannelArgs,
     MutationUpdateCollectionArgs,
     Permission,
     QueryCollectionArgs,
@@ -150,4 +150,24 @@ export class CollectionResolver {
         }
         return collection;
     };
+
+    @Transaction()
+    @Mutation()
+    @Allow(Permission.CreateCatalog, Permission.CreateCollection)
+    async assignCollectionsToChannel(
+        @Ctx() ctx: RequestContext,
+        @Args() args: MutationAssignCollectionsToChannelArgs,
+    ): Promise<Array<Translated<Collection>>> {
+        return await this.collectionService.assignCollectionsToChannel(ctx,args.input);
+    }
+
+    @Transaction()
+    @Mutation()
+    @Allow(Permission.DeleteCatalog, Permission.DeleteCollection)
+    async removeCollectionsFromChannel(
+        @Ctx() ctx: RequestContext,
+        @Args() args: MutationRemoveCollectionsFromChannelArgs,
+    ): Promise<Array<Translated<Collection>>> {
+        return await this.collectionService.removeCollectionsFromChannel(ctx, args.input);
+    }
 }

+ 24 - 1
packages/core/src/api/resolvers/admin/facet.resolver.ts

@@ -1,19 +1,23 @@
 import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 import {
     DeletionResponse,
+    MutationAssignFacetsToChannelArgs,
     MutationCreateFacetArgs,
     MutationCreateFacetValuesArgs,
     MutationDeleteFacetArgs,
     MutationDeleteFacetValuesArgs,
+    MutationRemoveFacetsFromChannelArgs,
     MutationUpdateFacetArgs,
     MutationUpdateFacetValuesArgs,
     Permission,
     QueryFacetArgs,
     QueryFacetsArgs,
+    RemoveFacetFromChannelResult,
 } from '@vendure/common/lib/generated-types';
 import { PaginatedList } from '@vendure/common/lib/shared-types';
 
 import { EntityNotFoundError } from '../../../common/error/errors';
+import { ErrorResultUnion } from '../../../common/index';
 import { Translated } from '../../../common/types/locale-types';
 import { ConfigService } from '../../../config/config.service';
 import { FacetValue } from '../../../entity/facet-value/facet-value.entity';
@@ -133,11 +137,30 @@ export class FacetResolver {
         @Ctx() ctx: RequestContext,
         @Args() args: MutationDeleteFacetValuesArgs,
     ): Promise<DeletionResponse[]> {
-        // return Promise.all(args.ids.map(id => this.facetValueService.delete(ctx, id, args.force || false)));
         const results: DeletionResponse[] = [];
         for (const id of args.ids) {
             results.push(await this.facetValueService.delete(ctx, id, args.force || false));
         }
         return results;
     }
+
+    @Transaction()
+    @Mutation()
+    @Allow(Permission.CreateCatalog, Permission.CreateFacet)
+    async assignFacetsToChannel(
+        @Ctx() ctx: RequestContext,
+        @Args() args: MutationAssignFacetsToChannelArgs,
+    ): Promise<Facet[]> {
+        return await this.facetService.assignFacetsToChannel(ctx, args.input);
+    }
+
+    @Transaction()
+    @Mutation()
+    @Allow(Permission.DeleteCatalog, Permission.DeleteFacet)
+    async removeFacetsFromChannel(
+        @Ctx() ctx: RequestContext,
+        @Args() args: MutationRemoveFacetsFromChannelArgs,
+    ): Promise<Array<ErrorResultUnion<RemoveFacetFromChannelResult, Facet>>> {
+        return await this.facetService.removeFacetsFromChannel(ctx, args.input);
+    }
 }

+ 16 - 0
packages/core/src/api/schema/admin-api/collection.api.graphql

@@ -19,6 +19,12 @@ type Mutation {
 
     "Move a Collection to a different parent or index"
     moveCollection(input: MoveCollectionInput!): Collection!
+
+    "Assigns Collections to the specified Channel"
+    assignCollectionsToChannel(input: AssignCollectionsToChannelInput!): [Collection!]!
+
+    "Removes Collections from the specified Channel"
+    removeCollectionsFromChannel(input: RemoveCollectionsFromChannelInput!): [Collection!]!
 }
 
 # generated by generateListOptions function
@@ -68,3 +74,13 @@ input UpdateCollectionInput {
     filters: [ConfigurableOperationInput!]
     translations: [UpdateCollectionTranslationInput!]
 }
+
+input AssignCollectionsToChannelInput {
+    collectionIds: [ID!]!
+    channelId: ID!
+}
+
+input RemoveCollectionsFromChannelInput {
+    collectionIds: [ID!]!
+    channelId: ID!
+}

+ 26 - 0
packages/core/src/api/schema/admin-api/facet.api.graphql

@@ -21,6 +21,12 @@ type Mutation {
 
     "Delete one or more FacetValues"
     deleteFacetValues(ids: [ID!]!, force: Boolean): [DeletionResponse!]!
+
+    "Assigns Facets to the specified Channel"
+    assignFacetsToChannel(input: AssignFacetsToChannelInput!): [Facet!]!
+
+    "Removes Facets from the specified Channel"
+    removeFacetsFromChannel(input: RemoveFacetsFromChannelInput!): [RemoveFacetFromChannelResult!]!
 }
 
 # generated by generateListOptions function
@@ -67,3 +73,23 @@ input UpdateFacetValueInput {
     code: String
     translations: [FacetValueTranslationInput!]
 }
+
+input AssignFacetsToChannelInput {
+    facetIds: [ID!]!
+    channelId: ID!
+}
+
+input RemoveFacetsFromChannelInput {
+    facetIds: [ID!]!
+    channelId: ID!
+    force: Boolean
+}
+
+type FacetInUseError implements ErrorResult {
+    errorCode: ErrorCode!
+    message: String!
+    productCount: Int!
+    variantCount: Int!
+}
+
+union RemoveFacetFromChannelResult = Facet | FacetInUseError

+ 18 - 1
packages/core/src/common/error/generated-graphql-admin-errors.ts

@@ -128,6 +128,18 @@ export class EmptyOrderLineSelectionError extends ErrorResult {
   }
 }
 
+export class FacetInUseError extends ErrorResult {
+  readonly __typename = 'FacetInUseError';
+  readonly errorCode = 'FACET_IN_USE_ERROR' as any;
+  readonly message = 'FACET_IN_USE_ERROR';
+  constructor(
+    public productCount: Scalars['Int'],
+    public variantCount: Scalars['Int'],
+  ) {
+    super();
+  }
+}
+
 export class FulfillmentStateTransitionError extends ErrorResult {
   readonly __typename = 'FulfillmentStateTransitionError';
   readonly errorCode = 'FULFILLMENT_STATE_TRANSITION_ERROR' as any;
@@ -425,7 +437,7 @@ export class SettlePaymentError extends ErrorResult {
 }
 
 
-const errorTypeNames = new Set(['AlreadyRefundedError', 'CancelActiveOrderError', 'CancelPaymentError', 'ChannelDefaultLanguageError', 'CouponCodeExpiredError', 'CouponCodeInvalidError', 'CouponCodeLimitError', 'CreateFulfillmentError', 'EmailAddressConflictError', 'EmptyOrderLineSelectionError', 'FulfillmentStateTransitionError', 'InsufficientStockError', 'InsufficientStockOnHandError', 'InvalidCredentialsError', 'InvalidFulfillmentHandlerError', 'ItemsAlreadyFulfilledError', 'LanguageNotAvailableError', 'ManualPaymentStateError', 'MimeTypeError', 'MissingConditionsError', 'MultipleOrderError', 'NativeAuthStrategyError', 'NegativeQuantityError', 'NoChangesSpecifiedError', 'NothingToRefundError', 'OrderLimitError', 'OrderModificationStateError', 'OrderStateTransitionError', 'PaymentMethodMissingError', 'PaymentOrderMismatchError', 'PaymentStateTransitionError', 'ProductOptionInUseError', 'QuantityTooGreatError', 'RefundOrderStateError', 'RefundPaymentIdMissingError', 'RefundStateTransitionError', 'SettlePaymentError']);
+const errorTypeNames = new Set(['AlreadyRefundedError', 'CancelActiveOrderError', 'CancelPaymentError', 'ChannelDefaultLanguageError', 'CouponCodeExpiredError', 'CouponCodeInvalidError', 'CouponCodeLimitError', 'CreateFulfillmentError', 'EmailAddressConflictError', 'EmptyOrderLineSelectionError', 'FacetInUseError', 'FulfillmentStateTransitionError', 'InsufficientStockError', 'InsufficientStockOnHandError', 'InvalidCredentialsError', 'InvalidFulfillmentHandlerError', 'ItemsAlreadyFulfilledError', 'LanguageNotAvailableError', 'ManualPaymentStateError', 'MimeTypeError', 'MissingConditionsError', 'MultipleOrderError', 'NativeAuthStrategyError', 'NegativeQuantityError', 'NoChangesSpecifiedError', 'NothingToRefundError', 'OrderLimitError', 'OrderModificationStateError', 'OrderStateTransitionError', 'PaymentMethodMissingError', 'PaymentOrderMismatchError', 'PaymentStateTransitionError', 'ProductOptionInUseError', 'QuantityTooGreatError', 'RefundOrderStateError', 'RefundPaymentIdMissingError', 'RefundStateTransitionError', 'SettlePaymentError']);
 function isGraphQLError(input: any): input is import('@vendure/common/lib/generated-types').ErrorResult {
   return input instanceof ErrorResult || errorTypeNames.has(input.__typename);
 }
@@ -466,6 +478,11 @@ export const adminErrorOperationTypeResolvers = {
       return isGraphQLError(value) ? (value as any).__typename : 'Customer';
     },
   },
+  RemoveFacetFromChannelResult: {
+    __resolveType(value: any) {
+      return isGraphQLError(value) ? (value as any).__typename : 'Facet';
+    },
+  },
   UpdateGlobalSettingsResult: {
     __resolveType(value: any) {
       return isGraphQLError(value) ? (value as any).__typename : 'GlobalSettings';

+ 91 - 1
packages/core/src/service/services/collection.service.ts

@@ -1,12 +1,15 @@
 import { Injectable, OnModuleInit } from '@nestjs/common';
 import {
+    AssignCollectionsToChannelInput,
     ConfigurableOperation,
     ConfigurableOperationDefinition,
     CreateCollectionInput,
     DeletionResponse,
     DeletionResult,
     MoveCollectionInput,
+    Permission,
     PreviewCollectionVariantsInput,
+    RemoveCollectionsFromChannelInput,
     UpdateCollectionInput,
 } from '@vendure/common/lib/generated-types';
 import { pick } from '@vendure/common/lib/pick';
@@ -17,7 +20,7 @@ import { debounceTime } from 'rxjs/operators';
 
 import { RequestContext, SerializedRequestContext } from '../../api/common/request-context';
 import { RelationPaths } from '../../api/index';
-import { IllegalOperationError } from '../../common/error/errors';
+import { ForbiddenError, IllegalOperationError, UserInputError } from '../../common/error/errors';
 import { ListQueryOptions } from '../../common/types/common-types';
 import { Translated } from '../../common/types/locale-types';
 import { assertFound, idsAreEqual } from '../../common/utils';
@@ -45,6 +48,7 @@ import { moveToIndex } from '../helpers/utils/move-to-index';
 import { AssetService } from './asset.service';
 import { ChannelService } from './channel.service';
 import { FacetValueService } from './facet-value.service';
+import { RoleService } from './role.service';
 
 export type ApplyCollectionFiltersJobData = {
     ctx: SerializedRequestContext;
@@ -77,6 +81,7 @@ export class CollectionService implements OnModuleInit {
         private configArgService: ConfigArgService,
         private customFieldRelationService: CustomFieldRelationService,
         private translator: TranslatorService,
+        private roleService: RoleService,
     ) {}
 
     /**
@@ -709,4 +714,89 @@ export class CollectionService implements OnModuleInit {
         this.rootCollection = this.translator.translate(newRoot, ctx);
         return this.rootCollection;
     }
+
+    /**
+     * @description
+     * Assigns Collections to the specified Channel
+     */
+    async assignCollectionsToChannel(
+        ctx: RequestContext,
+        input: AssignCollectionsToChannelInput,
+    ): Promise<Array<Translated<Collection>>> {
+        const hasPermission = await this.roleService.userHasAnyPermissionsOnChannel(ctx, input.channelId, [
+            Permission.UpdateCollection,
+            Permission.UpdateCatalog,
+        ]);
+        if (!hasPermission) {
+            throw new ForbiddenError();
+        }
+        const collectionsToAssign = await this.connection
+            .getRepository(ctx, Collection)
+            .findByIds(input.collectionIds);
+
+        await Promise.all(
+            collectionsToAssign.map(collection =>
+                this.channelService.assignToChannels(ctx, Collection, collection.id, [input.channelId]),
+            ),
+        );
+
+        await this.applyFiltersQueue.add({
+            ctx: ctx.serialize(),
+            collectionIds: collectionsToAssign.map(collection => collection.id),
+        });
+
+        return this.connection
+            .findByIdsInChannel(
+                ctx,
+                Collection,
+                collectionsToAssign.map(c => c.id),
+                ctx.channelId,
+                {},
+            )
+            .then(collections => collections.map(collection => this.translator.translate(collection, ctx)));
+    }
+
+    /**
+     * @description
+     * Remove Collections from the specified Channel
+     */
+    async removeCollectionsFromChannel(
+        ctx: RequestContext,
+        input: RemoveCollectionsFromChannelInput,
+    ): Promise<Array<Translated<Collection>>> {
+        const hasPermission = await this.roleService.userHasAnyPermissionsOnChannel(ctx, input.channelId, [
+            Permission.DeleteCollection,
+            Permission.DeleteCatalog,
+        ]);
+        if (!hasPermission) {
+            throw new ForbiddenError();
+        }
+        const defaultChannel = await this.channelService.getDefaultChannel(ctx);
+        if (idsAreEqual(input.channelId, defaultChannel.id)) {
+            throw new UserInputError('error.collections-cannot-be-removed-from-default-channel');
+        }
+        const collectionsToRemove = await this.connection
+            .getRepository(ctx, Collection)
+            .findByIds(input.collectionIds);
+
+        await Promise.all(
+            collectionsToRemove.map(async collection => {
+                const affectedVariantIds = await this.getCollectionProductVariantIds(collection);
+                await this.channelService.removeFromChannels(ctx, Collection, collection.id, [
+                    input.channelId,
+                ]);
+                this.eventBus.publish(new CollectionModificationEvent(ctx, collection, affectedVariantIds));
+            }),
+        );
+
+        return this.connection
+            .findByIdsInChannel(
+                ctx,
+                Collection,
+                collectionsToRemove.map(c => c.id),
+                ctx.channelId,
+                {},
+            )
+            .then(collections => collections.map(collection => this.translator.translate(collection, ctx)));
+    }
 }

+ 101 - 0
packages/core/src/service/services/facet.service.ts

@@ -1,15 +1,20 @@
 import { Injectable } from '@nestjs/common';
 import {
+    AssignFacetsToChannelInput,
     CreateFacetInput,
     DeletionResponse,
     DeletionResult,
     LanguageCode,
+    Permission,
+    RemoveFacetFromChannelResult,
+    RemoveFacetsFromChannelInput,
     UpdateFacetInput,
 } from '@vendure/common/lib/generated-types';
 import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
 
 import { RequestContext } from '../../api/common/request-context';
 import { RelationPaths } from '../../api/index';
+import { ErrorResultUnion, FacetInUseError, ForbiddenError, UserInputError } from '../../common';
 import { ListQueryOptions } from '../../common/types/common-types';
 import { Translated } from '../../common/types/locale-types';
 import { assertFound, idsAreEqual } from '../../common/utils';
@@ -27,6 +32,7 @@ import { translateDeep } from '../helpers/utils/translate-entity';
 
 import { ChannelService } from './channel.service';
 import { FacetValueService } from './facet-value.service';
+import { RoleService } from './role.service';
 
 /**
  * @description
@@ -46,6 +52,7 @@ export class FacetService {
         private customFieldRelationService: CustomFieldRelationService,
         private eventBus: EventBus,
         private translator: TranslatorService,
+        private roleService: RoleService,
     ) {}
 
     findAll(
@@ -240,4 +247,98 @@ export class FacetService {
 
         return candidate;
     }
+
+    /**
+     * @description
+     * Assigns Facets to the specified Channel
+     */
+    async assignFacetsToChannel(
+        ctx: RequestContext,
+        input: AssignFacetsToChannelInput,
+    ): Promise<Array<Translated<Facet>>> {
+        const hasPermission = await this.roleService.userHasAnyPermissionsOnChannel(ctx, input.channelId, [
+            Permission.UpdateFacet,
+            Permission.UpdateCatalog,
+        ]);
+        if (!hasPermission) {
+            throw new ForbiddenError();
+        }
+        const facetsToAssign = await this.connection.getRepository(ctx, Facet).findByIds(input.facetIds);
+
+        await Promise.all(
+            facetsToAssign.map(async facet => {
+                return this.channelService.assignToChannels(ctx, Facet, facet.id, [input.channelId]);
+            }),
+        );
+
+        return this.connection
+            .findByIdsInChannel(
+                ctx,
+                Facet,
+                facetsToAssign.map(f => f.id),
+                ctx.channelId,
+                {},
+            )
+            .then(facets => facets.map(facet => translateDeep(facet, ctx.languageCode)));
+    }
+
+    /**
+     * @description
+     * Remove Facets from the specified Channel
+     */
+    async removeFacetsFromChannel(
+        ctx: RequestContext,
+        input: RemoveFacetsFromChannelInput,
+    ): Promise<Array<ErrorResultUnion<RemoveFacetFromChannelResult, Facet>>> {
+        const hasPermission = await this.roleService.userHasAnyPermissionsOnChannel(ctx, input.channelId, [
+            Permission.DeleteFacet,
+            Permission.DeleteCatalog,
+        ]);
+        if (!hasPermission) {
+            throw new ForbiddenError();
+        }
+        const defaultChannel = await this.channelService.getDefaultChannel(ctx);
+        if (idsAreEqual(input.channelId, defaultChannel.id)) {
+            throw new UserInputError('error.facets-cannot-be-removed-from-default-channel');
+        }
+        const facets = await this.connection.getRepository(ctx, Facet).findByIds(input.facetIds);
+
+        const results: Array<ErrorResultUnion<RemoveFacetFromChannelResult, Facet>> = [];
+
+        for (const facet of facets) {
+            let productCount = 0;
+            let variantCount = 0;
+            if (facet.values.length) {
+                const counts = await this.facetValueService.checkFacetValueUsage(
+                    ctx,
+                    facet.values.map(fv => fv.id),
+                );
+                productCount = counts.productCount;
+                variantCount = counts.variantCount;
+
+                const isInUse = !!(productCount || variantCount);
+                const both = !!(productCount && variantCount) ? 'both' : 'single';
+                const i18nVars = { products: productCount, variants: variantCount, both };
+                let result: Translated<Facet> | undefined;
+
+                if (!isInUse) {
+                    await this.channelService.removeFromChannels(ctx, Facet, facet.id, [input.channelId]);
+                    result = await this.findOne(ctx, facet.id);
+                    if (result) {
+                        results.push(result);
+                    }
+                } else if (input?.force) {
+                    await this.channelService.removeFromChannels(ctx, Facet, facet.id, [input.channelId]);
+                    result = await this.findOne(ctx, facet.id);
+                    if (result) {
+                        results.push(result);
+                    }
+                } else {
+                    results.push(new FacetInUseError(productCount, variantCount));
+                }
+            }
+        }
+
+        return results;
+    }
 }

+ 20 - 3
packages/core/src/service/services/role.service.ts

@@ -127,6 +127,18 @@ export class RoleService {
         ctx: RequestContext,
         channelId: ID,
         permission: Permission,
+    ): Promise<boolean> {
+        return this.userHasAnyPermissionsOnChannel(ctx, channelId, [permission]);
+    }
+
+    /**
+     * @description
+     * Returns true if the User has any of the specified permission on that Channel
+     */
+    async userHasAnyPermissionsOnChannel(
+        ctx: RequestContext,
+        channelId: ID,
+        permissions: Permission[],
     ): Promise<boolean> {
         if (ctx.activeUserId == null) {
             return false;
@@ -139,7 +151,12 @@ export class RoleService {
         if (!channel) {
             return false;
         }
-        return channel.permissions.includes(permission);
+        for (const permission of permissions) {
+            if (channel.permissions.includes(permission)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     async create(ctx: RequestContext, input: CreateRoleInput): Promise<Role> {
@@ -229,8 +246,8 @@ export class RoleService {
     }
 
     private getRoleByCode(ctx: RequestContext | undefined, code: string) {
-        const repository = ctx 
-            ? this.connection.getRepository(ctx, Role) 
+        const repository = ctx
+            ? this.connection.getRepository(ctx, Role)
             : this.connection.rawConnection.getRepository(Role);
 
         return repository.findOne({

File diff suppressed because it is too large
+ 501 - 505
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts


File diff suppressed because it is too large
+ 501 - 505
packages/payments-plugin/e2e/graphql/generated-admin-types.ts


File diff suppressed because it is too large
+ 699 - 714
packages/payments-plugin/e2e/graphql/generated-shop-types.ts


File diff suppressed because it is too large
+ 736 - 751
packages/payments-plugin/src/mollie/graphql/generated-shop-types.ts


File diff suppressed because it is too large
+ 0 - 0
schema-admin.json


Some files were not shown because too many files changed in this diff