Parcourir la source

feat(core): Implement CustomerGroup queries & mutations

Relates to #330
Michael Bromley il y a 5 ans
Parent
commit
13342c0150

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

@@ -962,6 +962,38 @@ export type CustomerGroup = Node & {
   createdAt: Scalars['DateTime'];
   updatedAt: Scalars['DateTime'];
   name: Scalars['String'];
+  customers: CustomerList;
+};
+
+
+export type CustomerGroupCustomersArgs = {
+  options?: Maybe<CustomerListOptions>;
+};
+
+export type CustomerGroupFilterParameter = {
+  createdAt?: Maybe<DateOperators>;
+  updatedAt?: Maybe<DateOperators>;
+  name?: Maybe<StringOperators>;
+};
+
+export type CustomerGroupList = PaginatedList & {
+   __typename?: 'CustomerGroupList';
+  items: Array<CustomerGroup>;
+  totalItems: Scalars['Int'];
+};
+
+export type CustomerGroupListOptions = {
+  skip?: Maybe<Scalars['Int']>;
+  take?: Maybe<Scalars['Int']>;
+  sort?: Maybe<CustomerGroupSortParameter>;
+  filter?: Maybe<CustomerGroupFilterParameter>;
+};
+
+export type CustomerGroupSortParameter = {
+  id?: Maybe<SortOrder>;
+  createdAt?: Maybe<SortOrder>;
+  updatedAt?: Maybe<SortOrder>;
+  name?: Maybe<SortOrder>;
 };
 
 export type CustomerList = PaginatedList & {
@@ -2864,7 +2896,7 @@ export type Query = {
   country?: Maybe<Country>;
   customer?: Maybe<Customer>;
   customerGroup?: Maybe<CustomerGroup>;
-  customerGroups: Array<CustomerGroup>;
+  customerGroups: CustomerGroupList;
   customers: CustomerList;
   facet?: Maybe<Facet>;
   facets: FacetList;
@@ -2963,6 +2995,11 @@ export type QueryCustomerGroupArgs = {
 };
 
 
+export type QueryCustomerGroupsArgs = {
+  options?: Maybe<CustomerGroupListOptions>;
+};
+
+
 export type QueryCustomersArgs = {
   options?: Maybe<CustomerListOptions>;
 };

+ 4 - 1
packages/admin-ui/src/lib/core/src/common/introspection-result.ts

@@ -141,6 +141,9 @@ const result: IntrospectionResultData = {
                     {
                         name: 'HistoryEntryList',
                     },
+                    {
+                        name: 'CustomerList',
+                    },
                     {
                         name: 'CollectionList',
                     },
@@ -148,7 +151,7 @@ const result: IntrospectionResultData = {
                         name: 'CountryList',
                     },
                     {
-                        name: 'CustomerList',
+                        name: 'CustomerGroupList',
                     },
                     {
                         name: 'FacetList',

+ 42 - 1
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -953,6 +953,37 @@ export type CustomerGroup = Node & {
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     name: Scalars['String'];
+    customers: CustomerList;
+};
+
+export type CustomerGroupCustomersArgs = {
+    options?: Maybe<CustomerListOptions>;
+};
+
+export type CustomerGroupFilterParameter = {
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    name?: Maybe<StringOperators>;
+};
+
+export type CustomerGroupList = PaginatedList & {
+    __typename?: 'CustomerGroupList';
+    items: Array<CustomerGroup>;
+    totalItems: Scalars['Int'];
+};
+
+export type CustomerGroupListOptions = {
+    skip?: Maybe<Scalars['Int']>;
+    take?: Maybe<Scalars['Int']>;
+    sort?: Maybe<CustomerGroupSortParameter>;
+    filter?: Maybe<CustomerGroupFilterParameter>;
+};
+
+export type CustomerGroupSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
 };
 
 export type CustomerList = PaginatedList & {
@@ -1777,6 +1808,8 @@ export type Mutation = {
     createCustomerGroup: CustomerGroup;
     /** Update an existing CustomerGroup */
     updateCustomerGroup: CustomerGroup;
+    /** Delete a CustomerGroup */
+    deleteCustomerGroup: DeletionResponse;
     /** Add Customers to a CustomerGroup */
     addCustomersToGroup: CustomerGroup;
     /** Remove Customers from a CustomerGroup */
@@ -1965,6 +1998,10 @@ export type MutationUpdateCustomerGroupArgs = {
     input: UpdateCustomerGroupInput;
 };
 
+export type MutationDeleteCustomerGroupArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationAddCustomersToGroupArgs = {
     customerGroupId: Scalars['ID'];
     customerIds: Array<Scalars['ID']>;
@@ -2747,7 +2784,7 @@ export type Query = {
     collectionFilters: Array<ConfigurableOperationDefinition>;
     countries: CountryList;
     country?: Maybe<Country>;
-    customerGroups: Array<CustomerGroup>;
+    customerGroups: CustomerGroupList;
     customerGroup?: Maybe<CustomerGroup>;
     customers: CustomerList;
     customer?: Maybe<Customer>;
@@ -2824,6 +2861,10 @@ export type QueryCountryArgs = {
     id: Scalars['ID'];
 };
 
+export type QueryCustomerGroupsArgs = {
+    options?: Maybe<CustomerGroupListOptions>;
+};
+
 export type QueryCustomerGroupArgs = {
     id: Scalars['ID'];
 };

+ 34 - 1
packages/common/src/generated-shop-types.ts

@@ -661,12 +661,27 @@ export type CustomerOrdersArgs = {
     options?: Maybe<OrderListOptions>;
 };
 
+export type CustomerFilterParameter = {
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    title?: Maybe<StringOperators>;
+    firstName?: Maybe<StringOperators>;
+    lastName?: Maybe<StringOperators>;
+    phoneNumber?: Maybe<StringOperators>;
+    emailAddress?: Maybe<StringOperators>;
+};
+
 export type CustomerGroup = Node & {
     __typename?: 'CustomerGroup';
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     name: Scalars['String'];
+    customers: CustomerList;
+};
+
+export type CustomerGroupCustomersArgs = {
+    options?: Maybe<CustomerListOptions>;
 };
 
 export type CustomerList = PaginatedList & {
@@ -675,6 +690,24 @@ export type CustomerList = PaginatedList & {
     totalItems: Scalars['Int'];
 };
 
+export type CustomerListOptions = {
+    skip?: Maybe<Scalars['Int']>;
+    take?: Maybe<Scalars['Int']>;
+    sort?: Maybe<CustomerSortParameter>;
+    filter?: Maybe<CustomerFilterParameter>;
+};
+
+export type CustomerSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    title?: Maybe<SortOrder>;
+    firstName?: Maybe<SortOrder>;
+    lastName?: Maybe<SortOrder>;
+    phoneNumber?: Maybe<SortOrder>;
+    emailAddress?: Maybe<SortOrder>;
+};
+
 export type CustomField = {
     name: Scalars['String'];
     type: Scalars['String'];
@@ -1322,7 +1355,7 @@ export type Mutation = {
     removeOrderLine?: Maybe<Order>;
     /**
      * Adjusts an OrderLine. If custom fields are defined on the OrderLine entity, a
-     * third argument 'customFields' will be available.
+     * third argument 'customFields' of type `OrderLineCustomFieldsInput` will be available.
      */
     adjustOrderLine?: Maybe<Order>;
     /** Applies the given coupon code to the active Order */

+ 45 - 1
packages/common/src/generated-types.ts

@@ -954,6 +954,38 @@ export type CustomerGroup = Node & {
   createdAt: Scalars['DateTime'];
   updatedAt: Scalars['DateTime'];
   name: Scalars['String'];
+  customers: CustomerList;
+};
+
+
+export type CustomerGroupCustomersArgs = {
+  options?: Maybe<CustomerListOptions>;
+};
+
+export type CustomerGroupFilterParameter = {
+  createdAt?: Maybe<DateOperators>;
+  updatedAt?: Maybe<DateOperators>;
+  name?: Maybe<StringOperators>;
+};
+
+export type CustomerGroupList = PaginatedList & {
+   __typename?: 'CustomerGroupList';
+  items: Array<CustomerGroup>;
+  totalItems: Scalars['Int'];
+};
+
+export type CustomerGroupListOptions = {
+  skip?: Maybe<Scalars['Int']>;
+  take?: Maybe<Scalars['Int']>;
+  sort?: Maybe<CustomerGroupSortParameter>;
+  filter?: Maybe<CustomerGroupFilterParameter>;
+};
+
+export type CustomerGroupSortParameter = {
+  id?: Maybe<SortOrder>;
+  createdAt?: Maybe<SortOrder>;
+  updatedAt?: Maybe<SortOrder>;
+  name?: Maybe<SortOrder>;
 };
 
 export type CustomerList = PaginatedList & {
@@ -1774,6 +1806,8 @@ export type Mutation = {
   createCustomerGroup: CustomerGroup;
   /** Update an existing CustomerGroup */
   updateCustomerGroup: CustomerGroup;
+  /** Delete a CustomerGroup */
+  deleteCustomerGroup: DeletionResponse;
   /** Add Customers to a CustomerGroup */
   addCustomersToGroup: CustomerGroup;
   /** Remove Customers from a CustomerGroup */
@@ -1982,6 +2016,11 @@ export type MutationUpdateCustomerGroupArgs = {
 };
 
 
+export type MutationDeleteCustomerGroupArgs = {
+  id: Scalars['ID'];
+};
+
+
 export type MutationAddCustomersToGroupArgs = {
   customerGroupId: Scalars['ID'];
   customerIds: Array<Scalars['ID']>;
@@ -2823,7 +2862,7 @@ export type Query = {
   collectionFilters: Array<ConfigurableOperationDefinition>;
   countries: CountryList;
   country?: Maybe<Country>;
-  customerGroups: Array<CustomerGroup>;
+  customerGroups: CustomerGroupList;
   customerGroup?: Maybe<CustomerGroup>;
   customers: CustomerList;
   customer?: Maybe<Customer>;
@@ -2910,6 +2949,11 @@ export type QueryCountryArgs = {
 };
 
 
+export type QueryCustomerGroupsArgs = {
+  options?: Maybe<CustomerGroupListOptions>;
+};
+
+
 export type QueryCustomerGroupArgs = {
   id: Scalars['ID'];
 };

+ 270 - 0
packages/core/e2e/customer-group.e2e-spec.ts

@@ -0,0 +1,270 @@
+import {
+    AccountRegistrationEvent,
+    EventBus,
+    EventBusModule,
+    mergeConfig,
+    VendurePlugin,
+} from '@vendure/core';
+import { createTestEnvironment } from '@vendure/testing';
+import gql from 'graphql-tag';
+import cu from 'i18next-icu/locale-data/cu';
+import path from 'path';
+
+import { initialData } from '../../../e2e-common/e2e-initial-data';
+import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
+
+import {
+    AddCustomersToGroup,
+    CreateCustomerGroup,
+    DeleteCustomerGroup,
+    GetCustomerGroup,
+    GetCustomerGroups,
+    GetCustomerList,
+    RemoveCustomersFromGroup,
+    UpdateCustomerGroup,
+} from './graphql/generated-e2e-admin-types';
+import { DeletionResult } from './graphql/generated-e2e-shop-types';
+import { GET_CUSTOMER_LIST } from './graphql/shared-definitions';
+import { assertThrowsWithMessage } from './utils/assert-throws-with-message';
+import { sortById } from './utils/test-order-utils';
+
+describe('CustomerGroup resolver', () => {
+    const { server, adminClient, shopClient } = createTestEnvironment(testConfig);
+
+    let customers: GetCustomerList.Items[];
+
+    beforeAll(async () => {
+        await server.init({
+            initialData,
+            productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
+            customerCount: 5,
+        });
+        await adminClient.asSuperAdmin();
+        const result = await adminClient.query<GetCustomerList.Query>(GET_CUSTOMER_LIST);
+        customers = result.customers.items;
+    }, TEST_SETUP_TIMEOUT_MS);
+
+    afterAll(async () => {
+        await server.destroy();
+    });
+
+    it('create', async () => {
+        const { createCustomerGroup } = await adminClient.query<
+            CreateCustomerGroup.Mutation,
+            CreateCustomerGroup.Variables
+        >(CREATE_CUSTOMER_GROUP, {
+            input: {
+                name: 'group 1',
+                customerIds: [customers[0].id, customers[1].id],
+            },
+        });
+
+        expect(createCustomerGroup.name).toBe('group 1');
+        expect(createCustomerGroup.customers.items.sort(sortById)).toEqual([
+            { id: customers[0].id },
+            { id: customers[1].id },
+        ]);
+    });
+
+    it('customerGroups', async () => {
+        const { customerGroups } = await adminClient.query<
+            GetCustomerGroups.Query,
+            GetCustomerGroups.Variables
+        >(GET_CUSTOMER_GROUPS, {
+            options: {},
+        });
+
+        expect(customerGroups.totalItems).toBe(1);
+        expect(customerGroups.items[0].name).toBe('group 1');
+    });
+
+    it('customerGroup with customer list options', async () => {
+        const { customerGroup } = await adminClient.query<GetCustomerGroup.Query, GetCustomerGroup.Variables>(
+            GET_CUSTOMER_GROUP,
+            {
+                id: 'T_1',
+                options: {
+                    take: 1,
+                },
+            },
+        );
+
+        expect(customerGroup?.id).toBe('T_1');
+        expect(customerGroup?.name).toBe('group 1');
+        expect(customerGroup?.customers.items.length).toBe(1);
+        expect(customerGroup?.customers.totalItems).toBe(2);
+    });
+
+    it('update', async () => {
+        const { updateCustomerGroup } = await adminClient.query<
+            UpdateCustomerGroup.Mutation,
+            UpdateCustomerGroup.Variables
+        >(UPDATE_CUSTOMER_GROUP, {
+            input: {
+                id: 'T_1',
+                name: 'group 1 updated',
+            },
+        });
+
+        expect(updateCustomerGroup.name).toBe('group 1 updated');
+    });
+
+    it('addCustomersToGroup with existing customer', async () => {
+        const { addCustomersToGroup } = await adminClient.query<
+            AddCustomersToGroup.Mutation,
+            AddCustomersToGroup.Variables
+        >(ADD_CUSTOMERS_TO_GROUP, {
+            groupId: 'T_1',
+            customerIds: [customers[0].id],
+        });
+        expect(addCustomersToGroup.customers.items.sort(sortById)).toEqual([
+            { id: customers[0].id },
+            { id: customers[1].id },
+        ]);
+    });
+
+    it('addCustomersToGroup with new customers', async () => {
+        const { addCustomersToGroup } = await adminClient.query<
+            AddCustomersToGroup.Mutation,
+            AddCustomersToGroup.Variables
+        >(ADD_CUSTOMERS_TO_GROUP, {
+            groupId: 'T_1',
+            customerIds: [customers[2].id, customers[3].id],
+        });
+
+        expect(addCustomersToGroup.customers.items.sort(sortById)).toEqual([
+            { id: customers[0].id },
+            { id: customers[1].id },
+            { id: customers[2].id },
+            { id: customers[3].id },
+        ]);
+    });
+
+    it(
+        'removeCustomersFromGroup with invalid customerId',
+        assertThrowsWithMessage(async () => {
+            await adminClient.query<RemoveCustomersFromGroup.Mutation, RemoveCustomersFromGroup.Variables>(
+                REMOVE_CUSTOMERS_FROM_GROUP,
+                {
+                    groupId: 'T_1',
+                    customerIds: [customers[4].id],
+                },
+            );
+        }, `Customer does not belong to this CustomerGroup`),
+    );
+
+    it('removeCustomersFromGroup with valid customerIds', async () => {
+        const { removeCustomersFromGroup } = await adminClient.query<
+            RemoveCustomersFromGroup.Mutation,
+            RemoveCustomersFromGroup.Variables
+        >(REMOVE_CUSTOMERS_FROM_GROUP, {
+            groupId: 'T_1',
+            customerIds: [customers[1].id, customers[3].id],
+        });
+
+        expect(removeCustomersFromGroup.customers.items.sort(sortById)).toEqual([
+            { id: customers[0].id },
+            { id: customers[2].id },
+        ]);
+    });
+
+    it('deleteCustomerGroup', async () => {
+        const { deleteCustomerGroup } = await adminClient.query<
+            DeleteCustomerGroup.Mutation,
+            DeleteCustomerGroup.Variables
+        >(DELETE_CUSTOMER_GROUP, {
+            id: 'T_1',
+        });
+
+        expect(deleteCustomerGroup.message).toBeNull();
+        expect(deleteCustomerGroup.result).toBe(DeletionResult.DELETED);
+
+        const { customerGroups } = await adminClient.query<GetCustomerGroups.Query>(GET_CUSTOMER_GROUPS);
+        expect(customerGroups.totalItems).toBe(0);
+    });
+});
+
+export const CUSTOMER_GROUP_FRAGMENT = gql`
+    fragment CustomerGroup on CustomerGroup {
+        id
+        name
+        customers {
+            items {
+                id
+            }
+            totalItems
+        }
+    }
+`;
+
+export const CREATE_CUSTOMER_GROUP = gql`
+    mutation CreateCustomerGroup($input: CreateCustomerGroupInput!) {
+        createCustomerGroup(input: $input) {
+            ...CustomerGroup
+        }
+    }
+    ${CUSTOMER_GROUP_FRAGMENT}
+`;
+
+export const UPDATE_CUSTOMER_GROUP = gql`
+    mutation UpdateCustomerGroup($input: UpdateCustomerGroupInput!) {
+        updateCustomerGroup(input: $input) {
+            ...CustomerGroup
+        }
+    }
+    ${CUSTOMER_GROUP_FRAGMENT}
+`;
+
+export const DELETE_CUSTOMER_GROUP = gql`
+    mutation DeleteCustomerGroup($id: ID!) {
+        deleteCustomerGroup(id: $id) {
+            result
+            message
+        }
+    }
+`;
+
+export const GET_CUSTOMER_GROUPS = gql`
+    query GetCustomerGroups($options: CustomerGroupListOptions) {
+        customerGroups(options: $options) {
+            items {
+                id
+                name
+            }
+            totalItems
+        }
+    }
+`;
+
+export const GET_CUSTOMER_GROUP = gql`
+    query GetCustomerGroup($id: ID!, $options: CustomerListOptions) {
+        customerGroup(id: $id) {
+            id
+            name
+            customers(options: $options) {
+                items {
+                    id
+                }
+                totalItems
+            }
+        }
+    }
+`;
+
+export const ADD_CUSTOMERS_TO_GROUP = gql`
+    mutation AddCustomersToGroup($groupId: ID!, $customerIds: [ID!]!) {
+        addCustomersToGroup(customerGroupId: $groupId, customerIds: $customerIds) {
+            ...CustomerGroup
+        }
+    }
+    ${CUSTOMER_GROUP_FRAGMENT}
+`;
+
+export const REMOVE_CUSTOMERS_FROM_GROUP = gql`
+    mutation RemoveCustomersFromGroup($groupId: ID!, $customerIds: [ID!]!) {
+        removeCustomersFromGroup(customerGroupId: $groupId, customerIds: $customerIds) {
+            ...CustomerGroup
+        }
+    }
+    ${CUSTOMER_GROUP_FRAGMENT}
+`;

+ 168 - 1
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -953,6 +953,37 @@ export type CustomerGroup = Node & {
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     name: Scalars['String'];
+    customers: CustomerList;
+};
+
+export type CustomerGroupCustomersArgs = {
+    options?: Maybe<CustomerListOptions>;
+};
+
+export type CustomerGroupFilterParameter = {
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    name?: Maybe<StringOperators>;
+};
+
+export type CustomerGroupList = PaginatedList & {
+    __typename?: 'CustomerGroupList';
+    items: Array<CustomerGroup>;
+    totalItems: Scalars['Int'];
+};
+
+export type CustomerGroupListOptions = {
+    skip?: Maybe<Scalars['Int']>;
+    take?: Maybe<Scalars['Int']>;
+    sort?: Maybe<CustomerGroupSortParameter>;
+    filter?: Maybe<CustomerGroupFilterParameter>;
+};
+
+export type CustomerGroupSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
 };
 
 export type CustomerList = PaginatedList & {
@@ -1777,6 +1808,8 @@ export type Mutation = {
     createCustomerGroup: CustomerGroup;
     /** Update an existing CustomerGroup */
     updateCustomerGroup: CustomerGroup;
+    /** Delete a CustomerGroup */
+    deleteCustomerGroup: DeletionResponse;
     /** Add Customers to a CustomerGroup */
     addCustomersToGroup: CustomerGroup;
     /** Remove Customers from a CustomerGroup */
@@ -1965,6 +1998,10 @@ export type MutationUpdateCustomerGroupArgs = {
     input: UpdateCustomerGroupInput;
 };
 
+export type MutationDeleteCustomerGroupArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationAddCustomersToGroupArgs = {
     customerGroupId: Scalars['ID'];
     customerIds: Array<Scalars['ID']>;
@@ -2747,7 +2784,7 @@ export type Query = {
     collectionFilters: Array<ConfigurableOperationDefinition>;
     countries: CountryList;
     country?: Maybe<Country>;
-    customerGroups: Array<CustomerGroup>;
+    customerGroups: CustomerGroupList;
     customerGroup?: Maybe<CustomerGroup>;
     customers: CustomerList;
     customer?: Maybe<Customer>;
@@ -2824,6 +2861,10 @@ export type QueryCountryArgs = {
     id: Scalars['ID'];
 };
 
+export type QueryCustomerGroupsArgs = {
+    options?: Maybe<CustomerGroupListOptions>;
+};
+
 export type QueryCustomerGroupArgs = {
     id: Scalars['ID'];
 };
@@ -3793,6 +3834,79 @@ export type CreateCountryMutation = { __typename?: 'Mutation' } & {
     createCountry: { __typename?: 'Country' } & CountryFragment;
 };
 
+export type CustomerGroupFragment = { __typename?: 'CustomerGroup' } & Pick<CustomerGroup, 'id' | 'name'> & {
+        customers: { __typename?: 'CustomerList' } & Pick<CustomerList, 'totalItems'> & {
+                items: Array<{ __typename?: 'Customer' } & Pick<Customer, 'id'>>;
+            };
+    };
+
+export type CreateCustomerGroupMutationVariables = {
+    input: CreateCustomerGroupInput;
+};
+
+export type CreateCustomerGroupMutation = { __typename?: 'Mutation' } & {
+    createCustomerGroup: { __typename?: 'CustomerGroup' } & CustomerGroupFragment;
+};
+
+export type UpdateCustomerGroupMutationVariables = {
+    input: UpdateCustomerGroupInput;
+};
+
+export type UpdateCustomerGroupMutation = { __typename?: 'Mutation' } & {
+    updateCustomerGroup: { __typename?: 'CustomerGroup' } & CustomerGroupFragment;
+};
+
+export type DeleteCustomerGroupMutationVariables = {
+    id: Scalars['ID'];
+};
+
+export type DeleteCustomerGroupMutation = { __typename?: 'Mutation' } & {
+    deleteCustomerGroup: { __typename?: 'DeletionResponse' } & Pick<DeletionResponse, 'result' | 'message'>;
+};
+
+export type GetCustomerGroupsQueryVariables = {
+    options?: Maybe<CustomerGroupListOptions>;
+};
+
+export type GetCustomerGroupsQuery = { __typename?: 'Query' } & {
+    customerGroups: { __typename?: 'CustomerGroupList' } & Pick<CustomerGroupList, 'totalItems'> & {
+            items: Array<{ __typename?: 'CustomerGroup' } & Pick<CustomerGroup, 'id' | 'name'>>;
+        };
+};
+
+export type GetCustomerGroupQueryVariables = {
+    id: Scalars['ID'];
+    options?: Maybe<CustomerListOptions>;
+};
+
+export type GetCustomerGroupQuery = { __typename?: 'Query' } & {
+    customerGroup?: Maybe<
+        { __typename?: 'CustomerGroup' } & Pick<CustomerGroup, 'id' | 'name'> & {
+                customers: { __typename?: 'CustomerList' } & Pick<CustomerList, 'totalItems'> & {
+                        items: Array<{ __typename?: 'Customer' } & Pick<Customer, 'id'>>;
+                    };
+            }
+    >;
+};
+
+export type AddCustomersToGroupMutationVariables = {
+    groupId: Scalars['ID'];
+    customerIds: Array<Scalars['ID']>;
+};
+
+export type AddCustomersToGroupMutation = { __typename?: 'Mutation' } & {
+    addCustomersToGroup: { __typename?: 'CustomerGroup' } & CustomerGroupFragment;
+};
+
+export type RemoveCustomersFromGroupMutationVariables = {
+    groupId: Scalars['ID'];
+    customerIds: Array<Scalars['ID']>;
+};
+
+export type RemoveCustomersFromGroupMutation = { __typename?: 'Mutation' } & {
+    removeCustomersFromGroup: { __typename?: 'CustomerGroup' } & CustomerGroupFragment;
+};
+
 export type DeleteCustomerAddressMutationVariables = {
     id: Scalars['ID'];
 };
@@ -5646,6 +5760,59 @@ export namespace CreateCountry {
     export type CreateCountry = CountryFragment;
 }
 
+export namespace CustomerGroup {
+    export type Fragment = CustomerGroupFragment;
+    export type Customers = CustomerGroupFragment['customers'];
+    export type Items = NonNullable<CustomerGroupFragment['customers']['items'][0]>;
+}
+
+export namespace CreateCustomerGroup {
+    export type Variables = CreateCustomerGroupMutationVariables;
+    export type Mutation = CreateCustomerGroupMutation;
+    export type CreateCustomerGroup = CustomerGroupFragment;
+}
+
+export namespace UpdateCustomerGroup {
+    export type Variables = UpdateCustomerGroupMutationVariables;
+    export type Mutation = UpdateCustomerGroupMutation;
+    export type UpdateCustomerGroup = CustomerGroupFragment;
+}
+
+export namespace DeleteCustomerGroup {
+    export type Variables = DeleteCustomerGroupMutationVariables;
+    export type Mutation = DeleteCustomerGroupMutation;
+    export type DeleteCustomerGroup = DeleteCustomerGroupMutation['deleteCustomerGroup'];
+}
+
+export namespace GetCustomerGroups {
+    export type Variables = GetCustomerGroupsQueryVariables;
+    export type Query = GetCustomerGroupsQuery;
+    export type CustomerGroups = GetCustomerGroupsQuery['customerGroups'];
+    export type Items = NonNullable<GetCustomerGroupsQuery['customerGroups']['items'][0]>;
+}
+
+export namespace GetCustomerGroup {
+    export type Variables = GetCustomerGroupQueryVariables;
+    export type Query = GetCustomerGroupQuery;
+    export type CustomerGroup = NonNullable<GetCustomerGroupQuery['customerGroup']>;
+    export type Customers = NonNullable<GetCustomerGroupQuery['customerGroup']>['customers'];
+    export type Items = NonNullable<
+        NonNullable<GetCustomerGroupQuery['customerGroup']>['customers']['items'][0]
+    >;
+}
+
+export namespace AddCustomersToGroup {
+    export type Variables = AddCustomersToGroupMutationVariables;
+    export type Mutation = AddCustomersToGroupMutation;
+    export type AddCustomersToGroup = CustomerGroupFragment;
+}
+
+export namespace RemoveCustomersFromGroup {
+    export type Variables = RemoveCustomersFromGroupMutationVariables;
+    export type Mutation = RemoveCustomersFromGroupMutation;
+    export type RemoveCustomersFromGroup = CustomerGroupFragment;
+}
+
 export namespace DeleteCustomerAddress {
     export type Variables = DeleteCustomerAddressMutationVariables;
     export type Mutation = DeleteCustomerAddressMutation;

+ 34 - 1
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -661,12 +661,27 @@ export type CustomerOrdersArgs = {
     options?: Maybe<OrderListOptions>;
 };
 
+export type CustomerFilterParameter = {
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    title?: Maybe<StringOperators>;
+    firstName?: Maybe<StringOperators>;
+    lastName?: Maybe<StringOperators>;
+    phoneNumber?: Maybe<StringOperators>;
+    emailAddress?: Maybe<StringOperators>;
+};
+
 export type CustomerGroup = Node & {
     __typename?: 'CustomerGroup';
     id: Scalars['ID'];
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     name: Scalars['String'];
+    customers: CustomerList;
+};
+
+export type CustomerGroupCustomersArgs = {
+    options?: Maybe<CustomerListOptions>;
 };
 
 export type CustomerList = PaginatedList & {
@@ -675,6 +690,24 @@ export type CustomerList = PaginatedList & {
     totalItems: Scalars['Int'];
 };
 
+export type CustomerListOptions = {
+    skip?: Maybe<Scalars['Int']>;
+    take?: Maybe<Scalars['Int']>;
+    sort?: Maybe<CustomerSortParameter>;
+    filter?: Maybe<CustomerFilterParameter>;
+};
+
+export type CustomerSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    title?: Maybe<SortOrder>;
+    firstName?: Maybe<SortOrder>;
+    lastName?: Maybe<SortOrder>;
+    phoneNumber?: Maybe<SortOrder>;
+    emailAddress?: Maybe<SortOrder>;
+};
+
 export type CustomField = {
     name: Scalars['String'];
     type: Scalars['String'];
@@ -1322,7 +1355,7 @@ export type Mutation = {
     removeOrderLine?: Maybe<Order>;
     /**
      * Adjusts an OrderLine. If custom fields are defined on the OrderLine entity, a
-     * third argument 'customFields' will be available.
+     * third argument 'customFields' of type `OrderLineCustomFieldsInput` will be available.
      */
     adjustOrderLine?: Maybe<Order>;
     /** Applies the given coupon code to the active Order */

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

@@ -1,13 +1,14 @@
 /**
  * Helper method for creating tests which assert a given error message when the operation is attempted.
  */
-export function assertThrowsWithMessage(operation: () => Promise<any>, message: string) {
+export function assertThrowsWithMessage(operation: () => Promise<any>, message: string | (() => string)) {
     return async () => {
         try {
             await operation();
             fail('Should have thrown');
         } catch (err) {
-            expect(err.message).toEqual(expect.stringContaining(message));
+            const messageString = typeof message === 'function' ? message() : message;
+            expect(err.message).toEqual(expect.stringContaining(messageString));
         }
     };
 }

+ 2 - 0
packages/core/src/api/api-internal-modules.ts

@@ -32,6 +32,7 @@ import { TaxRateResolver } from './resolvers/admin/tax-rate.resolver';
 import { ZoneResolver } from './resolvers/admin/zone.resolver';
 import { CollectionEntityResolver } from './resolvers/entity/collection-entity.resolver';
 import { CustomerEntityResolver } from './resolvers/entity/customer-entity.resolver';
+import { CustomerGroupEntityResolver } from './resolvers/entity/customer-group-entity.resolver';
 import { FulfillmentEntityResolver } from './resolvers/entity/fulfillment-entity.resolver';
 import { OrderEntityResolver } from './resolvers/entity/order-entity.resolver';
 import { OrderLineEntityResolver } from './resolvers/entity/order-line-entity.resolver';
@@ -90,6 +91,7 @@ const shopResolvers = [
 export const entityResolvers = [
     CollectionEntityResolver,
     CustomerEntityResolver,
+    CustomerGroupEntityResolver,
     FulfillmentEntityResolver,
     OrderEntityResolver,
     OrderLineEntityResolver,

+ 15 - 2
packages/core/src/api/resolvers/admin/customer-group.resolver.ts

@@ -1,12 +1,16 @@
 import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
 import {
+    DeletionResponse,
     MutationAddCustomersToGroupArgs,
     MutationCreateCustomerGroupArgs,
+    MutationDeleteCustomerGroupArgs,
     MutationRemoveCustomersFromGroupArgs,
     MutationUpdateCustomerGroupArgs,
     Permission,
     QueryCustomerGroupArgs,
+    QueryCustomerGroupsArgs,
 } from '@vendure/common/lib/generated-types';
+import { PaginatedList } from '@vendure/common/lib/shared-types';
 
 import { CustomerGroup } from '../../../entity/customer-group/customer-group.entity';
 import { CustomerGroupService } from '../../../service/services/customer-group.service';
@@ -20,8 +24,11 @@ export class CustomerGroupResolver {
 
     @Query()
     @Allow(Permission.ReadCustomer)
-    customerGroups(@Ctx() ctx: RequestContext): Promise<CustomerGroup[]> {
-        return this.customerGroupService.findAll();
+    customerGroups(
+        @Ctx() ctx: RequestContext,
+        @Args() args: QueryCustomerGroupsArgs,
+    ): Promise<PaginatedList<CustomerGroup>> {
+        return this.customerGroupService.findAll(args.options || undefined);
     }
 
     @Query()
@@ -45,6 +52,12 @@ export class CustomerGroupResolver {
         return this.customerGroupService.update(args.input);
     }
 
+    @Mutation()
+    @Allow(Permission.DeleteCustomer)
+    async deleteCustomerGroup(@Args() args: MutationDeleteCustomerGroupArgs): Promise<DeletionResponse> {
+        return this.customerGroupService.delete(args.id);
+    }
+
     @Mutation()
     @Allow(Permission.UpdateCustomer)
     async addCustomersToGroup(@Args() args: MutationAddCustomersToGroupArgs): Promise<CustomerGroup> {

+ 23 - 0
packages/core/src/api/resolvers/entity/customer-group-entity.resolver.ts

@@ -0,0 +1,23 @@
+import { Args, Parent, ResolveField, Resolver } from '@nestjs/graphql';
+import { QueryCustomersArgs } from '@vendure/common/lib/generated-types';
+import { PaginatedList } from '@vendure/common/lib/shared-types';
+
+import { CustomerGroup } from '../../../entity/customer-group/customer-group.entity';
+import { Customer } from '../../../entity/customer/customer.entity';
+import { CustomerGroupService } from '../../../service/services/customer-group.service';
+import { RequestContext } from '../../common/request-context';
+import { Ctx } from '../../decorators/request-context.decorator';
+
+@Resolver('CustomerGroup')
+export class CustomerGroupEntityResolver {
+    constructor(private customerGroupService: CustomerGroupService) {}
+
+    @ResolveField()
+    async customers(
+        @Ctx() ctx: RequestContext,
+        @Parent() customerGroup: CustomerGroup,
+        @Args() args: QueryCustomersArgs,
+    ): Promise<PaginatedList<Customer>> {
+        return this.customerGroupService.getGroupCustomers(customerGroup.id, args.options || undefined);
+    }
+}

+ 11 - 1
packages/core/src/api/schema/admin-api/customer-group.api.graphql

@@ -1,5 +1,5 @@
 type Query {
-    customerGroups: [CustomerGroup!]!
+    customerGroups(options: CustomerGroupListOptions): CustomerGroupList!
     customerGroup(id: ID!): CustomerGroup
 }
 
@@ -8,12 +8,22 @@ type Mutation {
     createCustomerGroup(input: CreateCustomerGroupInput!): CustomerGroup!
     "Update an existing CustomerGroup"
     updateCustomerGroup(input: UpdateCustomerGroupInput!): CustomerGroup!
+    "Delete a CustomerGroup"
+    deleteCustomerGroup(id: ID!): DeletionResponse!
     "Add Customers to a CustomerGroup"
     addCustomersToGroup(customerGroupId: ID!, customerIds: [ID!]!): CustomerGroup!
     "Remove Customers from a CustomerGroup"
     removeCustomersFromGroup(customerGroupId: ID!, customerIds: [ID!]!): CustomerGroup!
 }
 
+type CustomerGroupList implements PaginatedList {
+    items: [CustomerGroup!]!
+    totalItems: Int!
+}
+
+# generated by generateListOptions function
+input CustomerGroupListOptions
+
 input CreateCustomerGroupInput {
     name: String!
     customerIds: [ID!]

+ 4 - 0
packages/core/src/api/schema/type/customer-group.type.graphql

@@ -3,4 +3,8 @@ type CustomerGroup implements Node {
     createdAt: DateTime!
     updatedAt: DateTime!
     name: String!
+    customers(options: CustomerListOptions): CustomerList!
 }
+
+# generated by generateListOptions function
+input CustomerListOptions

+ 1 - 1
packages/core/src/entity/customer-group/customer-group.entity.ts

@@ -19,6 +19,6 @@ export class CustomerGroup extends VendureEntity {
 
     @Column() name: string;
 
-    @ManyToMany(type => Customer)
+    @ManyToMany((type) => Customer, (customer) => customer.groups)
     customers: Customer[];
 }

+ 5 - 11
packages/core/src/entity/customer/customer.entity.ts

@@ -40,26 +40,20 @@ export class Customer extends VendureEntity implements HasCustomFields, SoftDele
     @Column()
     emailAddress: string;
 
-    @ManyToMany(type => CustomerGroup)
+    @ManyToMany((type) => CustomerGroup, (group) => group.customers)
     @JoinTable()
     groups: CustomerGroup[];
 
-    @OneToMany(
-        type => Address,
-        address => address.customer,
-    )
+    @OneToMany((type) => Address, (address) => address.customer)
     addresses: Address[];
 
-    @OneToMany(
-        type => Order,
-        order => order.customer,
-    )
+    @OneToMany((type) => Order, (order) => order.customer)
     orders: Order[];
 
-    @OneToOne(type => User, { eager: true })
+    @OneToOne((type) => User, { eager: true })
     @JoinColumn()
     user?: User;
 
-    @Column(type => CustomCustomerFields)
+    @Column((type) => CustomCustomerFields)
     customFields: CustomCustomerFields;
 }

+ 1 - 0
packages/core/src/i18n/messages/en.json

@@ -24,6 +24,7 @@
     "create-fulfillment-items-already-fulfilled": "One or more OrderItems have already been fulfilled",
     "create-fulfillment-orders-must-be-settled": "One or more OrderItems belong to an Order which is in an invalid state",
     "create-fulfillment-nothing-to-fulfill": "Nothing to fulfill",
+    "customer-does-not-belong-to-customer-group": "Customer does not belong to this CustomerGroup",
     "default-channel-not-found":  "Default channel not found",
     "email-address-must-be-unique": "The email address must be unique",
     "email-address-not-available": "This email address is not available",

+ 68 - 21
packages/core/src/service/services/customer-group.service.ts

@@ -2,38 +2,61 @@ import { Injectable } from '@nestjs/common';
 import { InjectConnection } from '@nestjs/typeorm';
 import {
     CreateCustomerGroupInput,
+    CustomerGroupListOptions,
+    CustomerListOptions,
+    DeletionResponse,
+    DeletionResult,
     MutationAddCustomersToGroupArgs,
     MutationRemoveCustomersFromGroupArgs,
     UpdateCustomerGroupInput,
 } from '@vendure/common/lib/generated-types';
-import { ID } from '@vendure/common/lib/shared-types';
-import { unique } from '@vendure/common/lib/unique';
+import { ID, PaginatedList } from '@vendure/common/lib/shared-types';
 import { Connection } from 'typeorm';
 
-import { assertFound } from '../../common/utils';
+import { UserInputError } from '../../common/error/errors';
+import { assertFound, idsAreEqual } from '../../common/utils';
 import { CustomerGroup } from '../../entity/customer-group/customer-group.entity';
 import { Customer } from '../../entity/customer/customer.entity';
+import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { getEntityOrThrow } from '../helpers/utils/get-entity-or-throw';
 import { patchEntity } from '../helpers/utils/patch-entity';
 
 @Injectable()
 export class CustomerGroupService {
-    constructor(@InjectConnection() private connection: Connection) {}
+    constructor(
+        @InjectConnection() private connection: Connection,
+        private listQueryBuilder: ListQueryBuilder,
+    ) {}
 
-    findAll(): Promise<CustomerGroup[]> {
-        return this.connection.getRepository(CustomerGroup).find({});
+    findAll(options?: CustomerGroupListOptions): Promise<PaginatedList<CustomerGroup>> {
+        return this.listQueryBuilder
+            .build(CustomerGroup, options)
+            .getManyAndCount()
+            .then(([items, totalItems]) => ({ items, totalItems }));
     }
 
     findOne(customerGroupId: ID): Promise<CustomerGroup | undefined> {
         return this.connection.getRepository(CustomerGroup).findOne(customerGroupId);
     }
 
+    getGroupCustomers(customerGroupId: ID, options?: CustomerListOptions): Promise<PaginatedList<Customer>> {
+        return this.listQueryBuilder
+            .build(Customer, options)
+            .leftJoin('customer.groups', 'group')
+            .andWhere('group.id = :groupId', { groupId: customerGroupId })
+            .getManyAndCount()
+            .then(([items, totalItems]) => ({ items, totalItems }));
+    }
+
     async create(input: CreateCustomerGroupInput): Promise<CustomerGroup> {
         const customerGroup = new CustomerGroup(input);
+
+        const newCustomerGroup = await this.connection.getRepository(CustomerGroup).save(customerGroup);
         if (input.customerIds) {
-            customerGroup.customers = await this.getCustomersFromIds(input.customerIds);
+            const customers = await this.getCustomersFromIds(input.customerIds);
+            customers.forEach((c) => (c.groups = [...(c.groups || []), newCustomerGroup]));
+            await this.connection.getRepository(Customer).save(customers);
         }
-        const newCustomerGroup = await this.connection.getRepository(CustomerGroup).save(customerGroup);
         return assertFound(this.findOne(newCustomerGroup.id));
     }
 
@@ -44,25 +67,49 @@ export class CustomerGroupService {
         return assertFound(this.findOne(customerGroup.id));
     }
 
+    async delete(id: ID): Promise<DeletionResponse> {
+        const group = await getEntityOrThrow(this.connection, CustomerGroup, id);
+        try {
+            await this.connection.getRepository(CustomerGroup).remove(group);
+            return {
+                result: DeletionResult.DELETED,
+            };
+        } catch (e) {
+            return {
+                result: DeletionResult.NOT_DELETED,
+                message: e.message,
+            };
+        }
+    }
+
     async addCustomersToGroup(input: MutationAddCustomersToGroupArgs): Promise<CustomerGroup> {
-        const countries = await this.getCustomersFromIds(input.customerIds);
-        const customerGroup = await getEntityOrThrow(this.connection, CustomerGroup, input.customerGroupId);
-        const customers = unique(customerGroup.customers.concat(countries), 'id');
-        customerGroup.customers = customers;
-        await this.connection.getRepository(CustomerGroup).save(customerGroup, { reload: false });
-        return customerGroup;
+        const customers = await this.getCustomersFromIds(input.customerIds);
+        const group = await getEntityOrThrow(this.connection, CustomerGroup, input.customerGroupId);
+        for (const customer of customers) {
+            if (!customer.groups.map((g) => g.id).includes(input.customerGroupId)) {
+                customer.groups.push(group);
+            }
+        }
+        await this.connection.getRepository(Customer).save(customers, { reload: false });
+        return assertFound(this.findOne(group.id));
     }
 
     async removeCustomersFromGroup(input: MutationRemoveCustomersFromGroupArgs): Promise<CustomerGroup> {
-        const customerGroup = await getEntityOrThrow(this.connection, CustomerGroup, input.customerGroupId);
-        customerGroup.customers = customerGroup.customers.filter(
-            customer => !input.customerIds.includes(customer.id as string),
-        );
-        await this.connection.getRepository(CustomerGroup).save(customerGroup, { reload: false });
-        return customerGroup;
+        const customers = await this.getCustomersFromIds(input.customerIds);
+        const group = await getEntityOrThrow(this.connection, CustomerGroup, input.customerGroupId);
+        for (const customer of customers) {
+            if (!customer.groups.map((g) => g.id).includes(input.customerGroupId)) {
+                throw new UserInputError('error.customer-does-not-belong-to-customer-group');
+            }
+            customer.groups = customer.groups.filter((g) => !idsAreEqual(g.id, group.id));
+        }
+        await this.connection.getRepository(Customer).save(customers, { reload: false });
+        return assertFound(this.findOne(group.id));
     }
 
     private getCustomersFromIds(ids: ID[]): Promise<Customer[]> {
-        return this.connection.getRepository(Customer).findByIds(ids, { where: { deletedAt: null } });
+        return this.connection
+            .getRepository(Customer)
+            .findByIds(ids, { where: { deletedAt: null }, relations: ['groups'] });
     }
 }

+ 1 - 1
packages/dev-server/dev-config.ts

@@ -123,7 +123,7 @@ function getDbConfig(): ConnectionOptions {
         default:
             console.log('Using mysql connection');
             return {
-                synchronize: true,
+                synchronize: false,
                 type: 'mysql',
                 host: '192.168.99.100',
                 port: 3306,

+ 42 - 1
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -953,6 +953,37 @@ export type CustomerGroup = Node & {
     createdAt: Scalars['DateTime'];
     updatedAt: Scalars['DateTime'];
     name: Scalars['String'];
+    customers: CustomerList;
+};
+
+export type CustomerGroupCustomersArgs = {
+    options?: Maybe<CustomerListOptions>;
+};
+
+export type CustomerGroupFilterParameter = {
+    createdAt?: Maybe<DateOperators>;
+    updatedAt?: Maybe<DateOperators>;
+    name?: Maybe<StringOperators>;
+};
+
+export type CustomerGroupList = PaginatedList & {
+    __typename?: 'CustomerGroupList';
+    items: Array<CustomerGroup>;
+    totalItems: Scalars['Int'];
+};
+
+export type CustomerGroupListOptions = {
+    skip?: Maybe<Scalars['Int']>;
+    take?: Maybe<Scalars['Int']>;
+    sort?: Maybe<CustomerGroupSortParameter>;
+    filter?: Maybe<CustomerGroupFilterParameter>;
+};
+
+export type CustomerGroupSortParameter = {
+    id?: Maybe<SortOrder>;
+    createdAt?: Maybe<SortOrder>;
+    updatedAt?: Maybe<SortOrder>;
+    name?: Maybe<SortOrder>;
 };
 
 export type CustomerList = PaginatedList & {
@@ -1777,6 +1808,8 @@ export type Mutation = {
     createCustomerGroup: CustomerGroup;
     /** Update an existing CustomerGroup */
     updateCustomerGroup: CustomerGroup;
+    /** Delete a CustomerGroup */
+    deleteCustomerGroup: DeletionResponse;
     /** Add Customers to a CustomerGroup */
     addCustomersToGroup: CustomerGroup;
     /** Remove Customers from a CustomerGroup */
@@ -1965,6 +1998,10 @@ export type MutationUpdateCustomerGroupArgs = {
     input: UpdateCustomerGroupInput;
 };
 
+export type MutationDeleteCustomerGroupArgs = {
+    id: Scalars['ID'];
+};
+
 export type MutationAddCustomersToGroupArgs = {
     customerGroupId: Scalars['ID'];
     customerIds: Array<Scalars['ID']>;
@@ -2747,7 +2784,7 @@ export type Query = {
     collectionFilters: Array<ConfigurableOperationDefinition>;
     countries: CountryList;
     country?: Maybe<Country>;
-    customerGroups: Array<CustomerGroup>;
+    customerGroups: CustomerGroupList;
     customerGroup?: Maybe<CustomerGroup>;
     customers: CustomerList;
     customer?: Maybe<Customer>;
@@ -2824,6 +2861,10 @@ export type QueryCountryArgs = {
     id: Scalars['ID'];
 };
 
+export type QueryCustomerGroupsArgs = {
+    options?: Maybe<CustomerGroupListOptions>;
+};
+
 export type QueryCustomerGroupArgs = {
     id: Scalars['ID'];
 };

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
schema-admin.json


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
schema-shop.json


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff