diff --git a/packages/core/e2e/duplicate-entity.e2e-spec.ts b/packages/core/e2e/duplicate-entity.e2e-spec.ts index ad2152b90c..4c4b376a04 100644 --- a/packages/core/e2e/duplicate-entity.e2e-spec.ts +++ b/packages/core/e2e/duplicate-entity.e2e-spec.ts @@ -35,6 +35,7 @@ import { CREATE_ROLE, GET_COLLECTION, GET_COLLECTIONS, + GET_FACET_WITH_VALUES, GET_PRODUCT_WITH_VARIANTS, UPDATE_PRODUCT_VARIANTS, } from './graphql/shared-definitions'; @@ -645,6 +646,72 @@ describe('Duplicating entities', () => { expect(collection?.filters).toEqual(testCollection.filters); }); }); + + describe('Facet duplicator', () => { + let newFacetId: string; + + it('duplicate facet', async () => { + const { duplicateEntity } = await adminClient.query< + Codegen.DuplicateEntityMutation, + Codegen.DuplicateEntityMutationVariables + >(DUPLICATE_ENTITY, { + input: { + entityName: 'Facet', + entityId: 'T_1', + duplicatorInput: { + code: 'facet-duplicator', + arguments: [ + { + name: 'includeFacetValues', + value: 'true', + }, + ], + }, + }, + }); + + duplicateEntityGuard.assertSuccess(duplicateEntity); + + expect(duplicateEntity.newEntityId).toBe('T_2'); + newFacetId = duplicateEntity.newEntityId; + }); + + it('facet name is suffixed', async () => { + const { facet } = await adminClient.query< + Codegen.GetFacetWithValuesQuery, + Codegen.GetFacetWithValuesQueryVariables + >(GET_FACET_WITH_VALUES, { + id: newFacetId, + }); + + expect(facet?.name).toBe('category (copy)'); + }); + + it('is initially private', async () => { + const { facet } = await adminClient.query< + Codegen.GetFacetWithValuesQuery, + Codegen.GetFacetWithValuesQueryVariables + >(GET_FACET_WITH_VALUES, { + id: newFacetId, + }); + + expect(facet?.isPrivate).toBe(true); + }); + + it('facet values are duplicated', async () => { + const { facet } = await adminClient.query< + Codegen.GetFacetWithValuesQuery, + Codegen.GetFacetWithValuesQueryVariables + >(GET_FACET_WITH_VALUES, { + id: newFacetId, + }); + + expect(facet?.values.map(v => v.name).sort()).toEqual([ + 'computers (copy)', + 'electronics (copy)', + ]); + }); + }); }); }); diff --git a/packages/core/e2e/facet.e2e-spec.ts b/packages/core/e2e/facet.e2e-spec.ts index 8e47b0b85d..290b2f7314 100644 --- a/packages/core/e2e/facet.e2e-spec.ts +++ b/packages/core/e2e/facet.e2e-spec.ts @@ -5,9 +5,9 @@ import path from 'path'; import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { initialData } from '../../../e2e-common/e2e-initial-data'; -import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config'; +import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config'; -import { FACET_VALUE_FRAGMENT, FACET_WITH_VALUES_FRAGMENT } from './graphql/fragments'; +import { FACET_VALUE_FRAGMENT } from './graphql/fragments'; import * as Codegen from './graphql/generated-e2e-admin-types'; import { ChannelFragment, @@ -15,7 +15,6 @@ import { DeletionResult, FacetWithValuesFragment, GetFacetWithValueListDocument, - GetFacetWithValuesDocument, LanguageCode, } from './graphql/generated-e2e-admin-types'; import { @@ -24,6 +23,7 @@ import { CREATE_FACET, GET_FACET_LIST, GET_FACET_LIST_SIMPLE, + GET_FACET_WITH_VALUES, GET_PRODUCT_WITH_VARIANTS, UPDATE_FACET, UPDATE_PRODUCT, @@ -810,15 +810,6 @@ describe('Facet resolver', () => { }); }); -export const GET_FACET_WITH_VALUES = gql` - query GetFacetWithValues($id: ID!) { - facet(id: $id) { - ...FacetWithValues - } - } - ${FACET_WITH_VALUES_FRAGMENT} -`; - export const GET_FACET_WITH_VALUE_LIST = gql` query GetFacetWithValueList($id: ID!) { facet(id: $id) { diff --git a/packages/core/e2e/graphql/shared-definitions.ts b/packages/core/e2e/graphql/shared-definitions.ts index 6411f3f453..aaa9d29d61 100644 --- a/packages/core/e2e/graphql/shared-definitions.ts +++ b/packages/core/e2e/graphql/shared-definitions.ts @@ -1054,3 +1054,12 @@ export const GET_COLLECTION = gql` } ${COLLECTION_FRAGMENT} `; + +export const GET_FACET_WITH_VALUES = gql` + query GetFacetWithValues($id: ID!) { + facet(id: $id) { + ...FacetWithValues + } + } + ${FACET_WITH_VALUES_FRAGMENT} +`; diff --git a/packages/core/src/config/entity/entity-duplicators/collection-duplicator.ts b/packages/core/src/config/entity/entity-duplicators/collection-duplicator.ts index 2460c2dcf3..ace2fa4bab 100644 --- a/packages/core/src/config/entity/entity-duplicators/collection-duplicator.ts +++ b/packages/core/src/config/entity/entity-duplicators/collection-duplicator.ts @@ -33,7 +33,7 @@ export const collectionDuplicator = new EntityDuplicator({ connection = injector.get(TransactionalConnection); collectionService = injector.get(CollectionService); }, - async duplicate({ ctx, id, args }) { + async duplicate({ ctx, id }) { const collection = await connection.getEntityOrThrow(ctx, Collection, id, { relations: { featuredAsset: true, diff --git a/packages/core/src/config/entity/entity-duplicators/facet-duplicator.ts b/packages/core/src/config/entity/entity-duplicators/facet-duplicator.ts new file mode 100644 index 0000000000..14553c0487 --- /dev/null +++ b/packages/core/src/config/entity/entity-duplicators/facet-duplicator.ts @@ -0,0 +1,84 @@ +import { + CreateFacetInput, + FacetTranslationInput, + LanguageCode, + Permission, +} from '@vendure/common/lib/generated-types'; + +import { Injector } from '../../../common/index'; +import { TransactionalConnection } from '../../../connection/index'; +import { Facet } from '../../../entity/index'; +import { FacetService, FacetValueService } from '../../../service/index'; +import { EntityDuplicator } from '../entity-duplicator'; + +let connection: TransactionalConnection; +let facetService: FacetService; +let facetValueService: FacetValueService; + +/** + * @description + * Duplicates a Facet + */ +export const facetDuplicator = new EntityDuplicator({ + code: 'facet-duplicator', + description: [ + { + languageCode: LanguageCode.en, + value: 'Default duplicator for Facets', + }, + ], + requiresPermission: [Permission.CreateFacet, Permission.CreateCatalog], + forEntities: ['Facet'], + args: { + includeFacetValues: { + type: 'boolean', + defaultValue: true, + label: [{ languageCode: LanguageCode.en, value: 'Include facet values' }], + }, + }, + init(injector: Injector) { + connection = injector.get(TransactionalConnection); + facetService = injector.get(FacetService); + facetValueService = injector.get(FacetValueService); + }, + async duplicate({ ctx, id, args }) { + const facet = await connection.getEntityOrThrow(ctx, Facet, id, { + relations: { + values: true, + }, + }); + const translations: FacetTranslationInput[] = facet.translations.map(translation => { + return { + name: translation.name + ' (copy)', + languageCode: translation.languageCode, + customFields: translation.customFields, + }; + }); + const facetInput: CreateFacetInput = { + isPrivate: true, + translations, + customFields: facet.customFields, + code: facet.code + '-copy', + }; + + const duplicatedFacet = await facetService.create(ctx, facetInput); + if (args.includeFacetValues) { + if (facet.values.length) { + for (const value of facet.values) { + const newValue = await facetValueService.create(ctx, duplicatedFacet, { + code: value.code + '-copy', + translations: value.translations.map(translation => ({ + name: translation.name + ' (copy)', + languageCode: translation.languageCode, + customFields: translation.customFields, + })), + facetId: duplicatedFacet.id, + customFields: value.customFields, + }); + duplicatedFacet.values.push(newValue); + } + } + } + return duplicatedFacet; + }, +}); diff --git a/packages/core/src/config/entity/entity-duplicators/index.ts b/packages/core/src/config/entity/entity-duplicators/index.ts index 757e2345a8..e86198f4e5 100644 --- a/packages/core/src/config/entity/entity-duplicators/index.ts +++ b/packages/core/src/config/entity/entity-duplicators/index.ts @@ -1,4 +1,5 @@ import { collectionDuplicator } from './collection-duplicator'; +import { facetDuplicator } from './facet-duplicator'; import { productDuplicator } from './product-duplicator'; -export const defaultEntityDuplicators = [productDuplicator, collectionDuplicator]; +export const defaultEntityDuplicators = [productDuplicator, collectionDuplicator, facetDuplicator];