Răsfoiți Sursa

docs: Add docs for entity duplication

Relates to #627
Michael Bromley 1 an în urmă
părinte
comite
c58ce5baaa
34 a modificat fișierele cu 599 adăugiri și 68 ștergeri
  1. 97 0
      docs/docs/reference/admin-ui-api/action-bar/action-bar-dropdown-menu-item.md
  2. 42 0
      docs/docs/reference/admin-ui-api/action-bar/add-action-bar-dropdown-menu-item.md
  3. 9 9
      docs/docs/reference/admin-ui-api/list-detail-views/detail-component-with-resolver.md
  4. 7 7
      docs/docs/reference/admin-ui-api/list-detail-views/typed-base-list-component.md
  5. 1 1
      docs/docs/reference/typescript-api/assets/asset-options.md
  6. 1 1
      docs/docs/reference/typescript-api/auth/auth-options.md
  7. 1 1
      docs/docs/reference/typescript-api/auth/cookie-options.md
  8. 1 1
      docs/docs/reference/typescript-api/auth/superadmin-credentials.md
  9. 1 1
      docs/docs/reference/typescript-api/common/job-state.md
  10. 1 1
      docs/docs/reference/typescript-api/common/language-code.md
  11. 1 1
      docs/docs/reference/typescript-api/common/permission.md
  12. 1 1
      docs/docs/reference/typescript-api/configuration/api-options.md
  13. 1 1
      docs/docs/reference/typescript-api/configuration/default-config.md
  14. 199 0
      docs/docs/reference/typescript-api/configuration/entity-duplicator.md
  15. 8 1
      docs/docs/reference/typescript-api/configuration/entity-options.md
  16. 1 1
      docs/docs/reference/typescript-api/configuration/runtime-vendure-config.md
  17. 1 1
      docs/docs/reference/typescript-api/configuration/system-options.md
  18. 1 1
      docs/docs/reference/typescript-api/configuration/vendure-config.md
  19. 2 1
      docs/docs/reference/typescript-api/custom-fields/index.md
  20. 4 4
      docs/docs/reference/typescript-api/fulfillment/fulfillment-handler.md
  21. 1 1
      docs/docs/reference/typescript-api/import-export/import-export-options.md
  22. 1 1
      docs/docs/reference/typescript-api/job-queue/job-queue-options.md
  23. 1 1
      docs/docs/reference/typescript-api/orders/order-options.md
  24. 1 1
      docs/docs/reference/typescript-api/payment/payment-options.md
  25. 1 1
      docs/docs/reference/typescript-api/products-stock/catalog-options.md
  26. 1 1
      docs/docs/reference/typescript-api/promotions/promotion-options.md
  27. 47 0
      docs/docs/reference/typescript-api/service-helpers/entity-duplicator-service.md
  28. 1 1
      docs/docs/reference/typescript-api/shipping/shipping-options.md
  29. 1 1
      docs/docs/reference/typescript-api/tax/tax-options.md
  30. 7 0
      packages/core/src/api/schema/admin-api/duplicate-entity.api.graphql
  31. 0 23
      packages/core/src/config/entity/entity-duplication-strategy.ts
  32. 131 1
      packages/core/src/config/entity/entity-duplicator.ts
  33. 9 2
      packages/core/src/config/vendure-config.ts
  34. 17 0
      packages/core/src/service/helpers/entity-duplicator/entity-duplicator.service.ts

+ 97 - 0
docs/docs/reference/admin-ui-api/action-bar/action-bar-dropdown-menu-item.md

@@ -0,0 +1,97 @@
+---
+title: "ActionBarDropdownMenuItem"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## ActionBarDropdownMenuItem
+
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="227" packageName="@vendure/admin-ui" since="2.2.0" />
+
+A dropdown menu item in the ActionBar area at the top of one of the list or detail views.
+
+```ts title="Signature"
+interface ActionBarDropdownMenuItem {
+    id: string;
+    label: string;
+    locationId: ActionBarLocationId;
+    hasDivider?: boolean;
+    buttonState?: (context: ActionBarContext) => Observable<ActionBarButtonState | undefined>;
+    onClick?: (event: MouseEvent, context: ActionBarContext) => void;
+    routerLink?: RouterLinkDefinition;
+    icon?: string;
+    requiresPermission?: string | string[];
+}
+```
+
+<div className="members-wrapper">
+
+### id
+
+<MemberInfo kind="property" type={`string`}   />
+
+A unique identifier for the item.
+### label
+
+<MemberInfo kind="property" type={`string`}   />
+
+The label to display for the item. This can also be a translation token,
+e.g. `invoice-plugin.print-invoice`.
+### locationId
+
+<MemberInfo kind="property" type={`<a href='/reference/admin-ui-api/action-bar/action-bar-location-id#actionbarlocationid'>ActionBarLocationId</a>`}   />
+
+The location in the UI where this menu item should be displayed.
+### hasDivider
+
+<MemberInfo kind="property" type={`boolean`}   />
+
+Whether to render a divider above this item.
+### buttonState
+
+<MemberInfo kind="property" type={`(context: <a href='/reference/admin-ui-api/action-bar/action-bar-context#actionbarcontext'>ActionBarContext</a>) =&#62; Observable&#60;ActionBarButtonState | undefined&#62;`}   />
+
+A function which returns an observable of the button state, allowing you to
+dynamically enable/disable or show/hide the button.
+### onClick
+
+<MemberInfo kind="property" type={`(event: MouseEvent, context: <a href='/reference/admin-ui-api/action-bar/action-bar-context#actionbarcontext'>ActionBarContext</a>) =&#62; void`}   />
+
+
+### routerLink
+
+<MemberInfo kind="property" type={`<a href='/reference/admin-ui-api/action-bar/router-link-definition#routerlinkdefinition'>RouterLinkDefinition</a>`}   />
+
+
+### icon
+
+<MemberInfo kind="property" type={`string`}   />
+
+An optional icon to display with the item. The icon
+should be a valid shape name from the [Clarity Icons](https://core.clarity.design/foundation/icons/shapes/)
+set.
+### requiresPermission
+
+<MemberInfo kind="property" type={`string | string[]`}   />
+
+Control the display of this item based on the user permissions. Note: if you attempt to pass a
+<a href='/reference/typescript-api/auth/permission-definition#permissiondefinition'>PermissionDefinition</a> object, you will get a compilation error. Instead, pass the plain
+string version. For example, if the permission is defined as:
+
+```ts
+export const MyPermission = new PermissionDefinition('ProductReview');
+```
+then the generated permission strings will be:
+
+- `CreateProductReview`
+- `ReadProductReview`
+- `UpdateProductReview`
+- `DeleteProductReview`
+
+
+</div>

+ 42 - 0
docs/docs/reference/admin-ui-api/action-bar/add-action-bar-dropdown-menu-item.md

@@ -0,0 +1,42 @@
+---
+title: "AddActionBarDropdownMenuItem"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## addActionBarDropdownMenuItem
+
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/extension/add-action-bar-dropdown-menu-item.ts" sourceLine="27" packageName="@vendure/admin-ui" since="2.2.0" />
+
+Adds a dropdown menu item to the ActionBar at the top right of each list or detail view. The locationId can
+be determined by pressing `ctrl + u` when running the Admin UI in dev mode.
+
+*Example*
+
+```ts title="providers.ts"
+import { addActionBarDropdownMenuItem } from '@vendure/admin-ui/core';
+
+export default [
+    addActionBarDropdownMenuItem({
+        id: 'print-invoice',
+        label: 'Print Invoice',
+        locationId: 'order-detail',
+        routerLink: ['/extensions/invoicing'],
+    }),
+];
+```
+
+```ts title="Signature"
+function addActionBarDropdownMenuItem(config: ActionBarDropdownMenuItem): Provider
+```
+Parameters
+
+### config
+
+<MemberInfo kind="parameter" type={`<a href='/reference/admin-ui-api/action-bar/action-bar-dropdown-menu-item#actionbardropdownmenuitem'>ActionBarDropdownMenuItem</a>`} />
+

+ 9 - 9
docs/docs/reference/admin-ui-api/list-detail-views/detail-component-with-resolver.md

@@ -13,8 +13,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-detail.component.ts" sourceLine="256" packageName="@vendure/admin-ui" />
 
-A helper function for creating tabs that point to a <a href='/reference/admin-ui-api/list-detail-views/typed-base-detail-component#typedbasedetailcomponent'>TypedBaseDetailComponent</a>. This takes
-care of the route resolver parts so that the detail component automatically has access to the
+A helper function for creating tabs that point to a <a href='/reference/admin-ui-api/list-detail-views/typed-base-detail-component#typedbasedetailcomponent'>TypedBaseDetailComponent</a>. This takes
+care of the route resolver parts so that the detail component automatically has access to the
 correct resolved detail data.
 
 *Example*
@@ -40,17 +40,17 @@ export class ProductSpecsUiExtensionModule {}
 ```
 
 ```ts title="Signature"
-function detailComponentWithResolver<T extends TypedDocumentNode<any, { id: string }>, Field extends keyof ResultOf<T>, R extends Field>(config: {
-    component: Type<TypedBaseDetailComponent<T, Field>>;
-    query: T;
-    entityKey: R;
-    getBreadcrumbs?: (entity: ResultOf<T>[R]) => BreadcrumbValue;
-    variables?: T extends TypedDocumentNode<any, infer V> ? Omit<V, 'id'> : never;
+function detailComponentWithResolver<T extends TypedDocumentNode<any, { id: string }>, Field extends keyof ResultOf<T>, R extends Field>(config: {
+    component: Type<TypedBaseDetailComponent<T, Field>>;
+    query: T;
+    entityKey: R;
+    getBreadcrumbs?: (entity: ResultOf<T>[R]) => BreadcrumbValue;
+    variables?: T extends TypedDocumentNode<any, infer V> ? Omit<V, 'id'> : never;
 }): void
 ```
 Parameters
 
 ### config
 
-<MemberInfo kind="parameter" type={`{     component: Type&#60;<a href='/reference/admin-ui-api/list-detail-views/typed-base-detail-component#typedbasedetailcomponent'>TypedBaseDetailComponent</a>&#60;T, Field&#62;&#62;;     query: T;     entityKey: R;     getBreadcrumbs?: (entity: ResultOf&#60;T&#62;[R]) =&#62; BreadcrumbValue;     variables?: T extends TypedDocumentNode&#60;any, infer V&#62; ? Omit&#60;V, 'id'&#62; : never; }`} />
+<MemberInfo kind="parameter" type={`{
     component: Type&#60;<a href='/reference/admin-ui-api/list-detail-views/typed-base-detail-component#typedbasedetailcomponent'>TypedBaseDetailComponent</a>&#60;T, Field&#62;&#62;;
     query: T;
     entityKey: R;
     getBreadcrumbs?: (entity: ResultOf&#60;T&#62;[R]) =&#62; BreadcrumbValue;
     variables?: T extends TypedDocumentNode&#60;any, infer V&#62; ? Omit&#60;V, 'id'&#62; : never;
 }`} />
 

+ 7 - 7
docs/docs/reference/admin-ui-api/list-detail-views/typed-base-list-component.md

@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-list.component.ts" sourceLine="199" packageName="@vendure/admin-ui" />
 
-A version of the <a href='/reference/admin-ui-api/list-detail-views/base-list-component#baselistcomponent'>BaseListComponent</a> which is designed to be used with a
+A version of the <a href='/reference/admin-ui-api/list-detail-views/base-list-component#baselistcomponent'>BaseListComponent</a> which is designed to be used with a
 [TypedDocumentNode](https://the-guild.dev/graphql/codegen/plugins/typescript/typed-document-node).
 
 ```ts title="Signature"
@@ -25,11 +25,11 @@ class TypedBaseListComponent<T extends TypedDocumentNode<any, Vars>, Field exten
     protected serverConfigService = inject(ServerConfigService);
     protected permissionsService = inject(PermissionsService);
     constructor()
-    configure(config: {
-        document: T;
-        getItems: (data: ResultOf<T>) => { items: Array<ItemOf<ResultOf<T>, Field>>; totalItems: number };
-        setVariables?: (skip: number, take: number) => VariablesOf<T>;
-        refreshListOnChanges?: Array<Observable<any>>;
+    configure(config: {
+        document: T;
+        getItems: (data: ResultOf<T>) => { items: Array<ItemOf<ResultOf<T>, Field>>; totalItems: number };
+        setVariables?: (skip: number, take: number) => VariablesOf<T>;
+        refreshListOnChanges?: Array<Observable<any>>;
     }) => ;
     ngOnInit() => ;
     createFilterCollection() => DataTableFilterCollection<NonNullable<NonNullable<Vars['options']>['filter']>>;
@@ -84,7 +84,7 @@ class TypedBaseListComponent<T extends TypedDocumentNode<any, Vars>, Field exten
 
 ### configure
 
-<MemberInfo kind="method" type={`(config: {         document: T;         getItems: (data: ResultOf&#60;T&#62;) =&#62; { items: Array&#60;ItemOf&#60;ResultOf&#60;T&#62;, Field&#62;&#62;; totalItems: number };         setVariables?: (skip: number, take: number) =&#62; VariablesOf&#60;T&#62;;         refreshListOnChanges?: Array&#60;Observable&#60;any&#62;&#62;;     }) => `}   />
+<MemberInfo kind="method" type={`(config: {
         document: T;
         getItems: (data: ResultOf&#60;T&#62;) =&#62; { items: Array&#60;ItemOf&#60;ResultOf&#60;T&#62;, Field&#62;&#62;; totalItems: number };
         setVariables?: (skip: number, take: number) =&#62; VariablesOf&#60;T&#62;;
         refreshListOnChanges?: Array&#60;Observable&#60;any&#62;&#62;;
     }) => `}   />
 
 
 ### ngOnInit

+ 1 - 1
docs/docs/reference/typescript-api/assets/asset-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AssetOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="626" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="627" packageName="@vendure/core" />
 
 The AssetOptions define how assets (images and other files) are named and stored, and how preview images are generated.
 

+ 1 - 1
docs/docs/reference/typescript-api/auth/auth-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## AuthOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="328" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="329" packageName="@vendure/core" />
 
 The AuthOptions define how authentication and authorization is managed.
 

+ 1 - 1
docs/docs/reference/typescript-api/auth/cookie-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CookieOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="223" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="224" packageName="@vendure/core" />
 
 Options for the handling of the cookies used to track sessions (only applicable if
 `authOptions.tokenMethod` is set to `'cookie'`). These options are passed directly

+ 1 - 1
docs/docs/reference/typescript-api/auth/superadmin-credentials.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## SuperadminCredentials
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="802" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="803" packageName="@vendure/core" />
 
 These credentials will be used to create the Superadmin user & administrator
 when Vendure first bootstraps.

+ 1 - 1
docs/docs/reference/typescript-api/common/job-state.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## JobState
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2131" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2161" packageName="@vendure/common" />
 
 The state of a Job in the JobQueue
 

+ 1 - 1
docs/docs/reference/typescript-api/common/language-code.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## LanguageCode
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2149" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2179" packageName="@vendure/common" />
 
 Languages in the form of a ISO 639-1 language code with optional
 region or script modifier (e.g. de_AT). The selection available is based

+ 1 - 1
docs/docs/reference/typescript-api/common/permission.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Permission
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="4277" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="4313" packageName="@vendure/common" />
 
 Permissions for administrators and customers. Used to control access to
 GraphQL resolvers via the <a href='/reference/typescript-api/request/allow-decorator#allow'>Allow</a> decorator.

+ 1 - 1
docs/docs/reference/typescript-api/configuration/api-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ApiOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="66" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="67" packageName="@vendure/core" />
 
 The ApiOptions define how the Vendure GraphQL APIs are exposed, as well as allowing the API layer
 to be extended with middleware.

+ 1 - 1
docs/docs/reference/typescript-api/configuration/default-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## defaultConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/default-config.ts" sourceLine="59" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/default-config.ts" sourceLine="60" packageName="@vendure/core" />
 
 The default configuration settings which are used if not explicitly overridden in the bootstrap() call.
 

+ 199 - 0
docs/docs/reference/typescript-api/configuration/entity-duplicator.md

@@ -0,0 +1,199 @@
+---
+title: "EntityDuplicator"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## EntityDuplicator
+
+<GenerationInfo sourceFile="packages/core/src/config/entity/entity-duplicator.ts" sourceLine="158" packageName="@vendure/core" since="2.2.0" />
+
+An EntityDuplicator is used to define the logic for duplicating entities when the `duplicateEntity` mutation is called.
+This allows you to add support for duplication of both core and custom entities.
+
+*Example*
+
+```ts title=src/config/custom-collection-duplicator.ts
+import { Collection, LanguageCode, Permission
+  EntityDuplicator, TransactionalConnection, CollectionService } from '@vendure/core';
+
+let collectionService: CollectionService;
+let connection: TransactionalConnection;
+
+// This is just an example - we already ship with a built-in duplicator for Collections.
+const customCollectionDuplicator = new EntityDuplicator({
+    code: 'custom-collection-duplicator',
+    description: [{ languageCode: LanguageCode.en, value: 'Custom collection duplicator' }],
+    args: {
+        throwError: {
+            type: 'boolean',
+            defaultValue: false,
+        },
+    },
+    forEntities: ['Collection'],
+    requiresPermission: [Permission.UpdateCollection],
+    init(injector) {
+        collectionService = injector.get(CollectionService);
+        connection = injector.get(TransactionalConnection);
+    },
+    duplicate: async input => {
+        const { ctx, id, args } = input;
+
+        const original = await connection.getEntityOrThrow(ctx, Collection, id, {
+            relations: {
+                assets: true,
+                featuredAsset: true,
+            },
+        });
+        const newCollection = await collectionService.create(ctx, {
+            isPrivate: original.isPrivate,
+            customFields: original.customFields,
+            assetIds: original.assets.map(a => a.id),
+            featuredAssetId: original.featuredAsset?.id,
+            parentId: original.parentId,
+            filters: original.filters.map(f => ({
+                code: f.code,
+                arguments: f.args,
+            })),
+            inheritFilters: original.inheritFilters,
+            translations: original.translations.map(t => ({
+                languageCode: t.languageCode,
+                name: `${t.name} (copy)`,
+                slug: `${t.slug}-copy`,
+                description: t.description,
+                customFields: t.customFields,
+            })),
+        });
+
+        if (args.throwError) {
+            // If an error is thrown at any point during the duplication process, the entire
+            // transaction will get automatically rolled back, and the mutation will return
+            // an ErrorResponse containing the error message.
+            throw new Error('Dummy error');
+        }
+
+        return newCollection;
+    },
+});
+```
+
+The duplicator then gets passed to your VendureConfig object:
+
+```ts title=src/vendure-config.ts
+import { VendureConfig, defaultEntityDuplicators } from '@vendure/core';
+import { customCollectionDuplicator } from './config/custom-collection-duplicator';
+
+export const config: VendureConfig = {
+   // ...
+   entityOptions: {
+     entityDuplicators: [
+         ...defaultEntityDuplicators,
+         customCollectionDuplicator,
+     ],
+   },
+};
+```
+
+```ts title="Signature"
+class EntityDuplicator<T extends ConfigArgs = ConfigArgs> extends ConfigurableOperationDef<T> {
+    constructor(config: EntityDuplicatorConfig<T>)
+    duplicate(input: {
+        ctx: RequestContext;
+        entityName: string;
+        id: ID;
+        args: ConfigArg[];
+    }) => Promise<VendureEntity>;
+}
+```
+* Extends: <code><a href='/reference/typescript-api/configurable-operation-def/#configurableoperationdef'>ConfigurableOperationDef</a>&#60;T&#62;</code>
+
+
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(config: <a href='/reference/typescript-api/configuration/entity-duplicator#entityduplicatorconfig'>EntityDuplicatorConfig</a>&#60;T&#62;) => EntityDuplicator`}   />
+
+
+### duplicate
+
+<MemberInfo kind="method" type={`(input: {         ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>;         entityName: string;         id: <a href='/reference/typescript-api/common/id#id'>ID</a>;         args: ConfigArg[];     }) => Promise&#60;<a href='/reference/typescript-api/entities/vendure-entity#vendureentity'>VendureEntity</a>&#62;`}   />
+
+
+
+
+</div>
+
+
+## DuplicateEntityFn
+
+<GenerationInfo sourceFile="packages/core/src/config/entity/entity-duplicator.ts" sourceLine="21" packageName="@vendure/core" since="2.2.0" />
+
+A function which performs the duplication of an entity.
+
+```ts title="Signature"
+type DuplicateEntityFn<T extends ConfigArgs> = (input: {
+    ctx: RequestContext;
+    entityName: string;
+    id: ID;
+    args: ConfigArgValues<T>;
+}) => Promise<VendureEntity>
+```
+
+
+## EntityDuplicatorConfig
+
+<GenerationInfo sourceFile="packages/core/src/config/entity/entity-duplicator.ts" sourceLine="36" packageName="@vendure/core" since="2.2.0" />
+
+Configuration for creating a new EntityDuplicator.
+
+```ts title="Signature"
+interface EntityDuplicatorConfig<T extends ConfigArgs> extends ConfigurableOperationDefOptions<T> {
+    requiresPermission: Array<Permission | string> | Permission | string;
+    forEntities: string[];
+    duplicate: DuplicateEntityFn<T>;
+}
+```
+* Extends: <code><a href='/reference/typescript-api/configurable-operation-def/configurable-operation-def-options#configurableoperationdefoptions'>ConfigurableOperationDefOptions</a>&#60;T&#62;</code>
+
+
+
+<div className="members-wrapper">
+
+### requiresPermission
+
+<MemberInfo kind="property" type={`Array&#60;<a href='/reference/typescript-api/common/permission#permission'>Permission</a> | string&#62; | <a href='/reference/typescript-api/common/permission#permission'>Permission</a> | string`}   />
+
+The permissions required in order to execute this duplicator. If an array is passed,
+then the administrator must have at least one of the permissions in the array.
+### forEntities
+
+<MemberInfo kind="property" type={`string[]`}   />
+
+The entities for which this duplicator is able to duplicate.
+### duplicate
+
+<MemberInfo kind="property" type={`<a href='/reference/typescript-api/configuration/entity-duplicator#duplicateentityfn'>DuplicateEntityFn</a>&#60;T&#62;`}   />
+
+The function which performs the duplication.
+
+*Example*
+
+```ts
+duplicate: async input => {
+  const { ctx, id, args } = input;
+
+  // perform the duplication logic here
+
+  return newEntity;
+}
+```
+
+
+</div>

+ 8 - 1
docs/docs/reference/typescript-api/configuration/entity-options.md

@@ -11,13 +11,14 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## EntityOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="944" packageName="@vendure/core" since="1.3.0" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="945" packageName="@vendure/core" since="1.3.0" />
 
 Options relating to the internal handling of entities.
 
 ```ts title="Signature"
 interface EntityOptions {
     entityIdStrategy?: EntityIdStrategy<any>;
+    entityDuplicators?: Array<EntityDuplicator<any>>;
     moneyStrategy?: MoneyStrategy;
     channelCacheTtl?: number;
     zoneCacheTtl?: number;
@@ -43,6 +44,12 @@ on an existing Vendure database will lead to problems with broken foreign-key
 references. To change primary key types like this, you'll need to start with
 a fresh database.
 :::
+### entityDuplicators
+
+<MemberInfo kind="property" type={`Array&#60;<a href='/reference/typescript-api/configuration/entity-duplicator#entityduplicator'>EntityDuplicator</a>&#60;any&#62;&#62;`} default="defaultEntityDuplicators"  since="2.2.0"  />
+
+An array of <a href='/reference/typescript-api/configuration/entity-duplicator#entityduplicator'>EntityDuplicator</a> instances which are used to duplicate entities
+when using the `duplicateEntity` mutation.
 ### moneyStrategy
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/money/money-strategy#moneystrategy'>MoneyStrategy</a>`} default="<a href='/reference/typescript-api/money/default-money-strategy#defaultmoneystrategy'>DefaultMoneyStrategy</a>"  since="2.0.0"  />

+ 1 - 1
docs/docs/reference/typescript-api/configuration/runtime-vendure-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## RuntimeVendureConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1182" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1192" packageName="@vendure/core" />
 
 This interface represents the VendureConfig object available at run-time, i.e. the user-supplied
 config values have been merged with the <a href='/reference/typescript-api/configuration/default-config#defaultconfig'>defaultConfig</a> values.

+ 1 - 1
docs/docs/reference/typescript-api/configuration/system-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## SystemOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1024" packageName="@vendure/core" since="1.6.0" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1034" packageName="@vendure/core" since="1.6.0" />
 
 Options relating to system functions.
 

+ 1 - 1
docs/docs/reference/typescript-api/configuration/vendure-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## VendureConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1052" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="1062" packageName="@vendure/core" />
 
 All possible configuration options are defined by the
 [`VendureConfig`](https://github.com/vendure-ecommerce/vendure/blob/master/server/src/config/vendure-config.ts) interface.

+ 2 - 1
docs/docs/reference/typescript-api/custom-fields/index.md

@@ -13,7 +13,8 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="159" packageName="@vendure/core" />
 
-Most entities can have additional fields added to them by defining an array of <a href='/reference/typescript-api/custom-fields/custom-field-config#customfieldconfig'>CustomFieldConfig</a>objects on against the corresponding key.
+Most entities can have additional fields added to them by defining an array of <a href='/reference/typescript-api/custom-fields/custom-field-config#customfieldconfig'>CustomFieldConfig</a>
+objects on against the corresponding key.
 
 *Example*
 

+ 4 - 4
docs/docs/reference/typescript-api/fulfillment/fulfillment-handler.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## FulfillmentHandler
 
-<GenerationInfo sourceFile="packages/core/src/config/fulfillment/fulfillment-handler.ts" sourceLine="150" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/fulfillment/fulfillment-handler.ts" sourceLine="149" packageName="@vendure/core" />
 
 A FulfillmentHandler is used when creating a new <a href='/reference/typescript-api/entities/fulfillment#fulfillment'>Fulfillment</a>. When the `addFulfillmentToOrder` mutation
 is executed, the specified handler will be used and it's `createFulfillment` method is called. This method
@@ -110,7 +110,7 @@ class FulfillmentHandler<T extends ConfigArgs = ConfigArgs> extends Configurable
 
 ## FulfillmentHandlerConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/fulfillment/fulfillment-handler.ts" sourceLine="49" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/fulfillment/fulfillment-handler.ts" sourceLine="48" packageName="@vendure/core" />
 
 The configuration object used to instantiate a <a href='/reference/typescript-api/fulfillment/fulfillment-handler#fulfillmenthandler'>FulfillmentHandler</a>.
 
@@ -152,7 +152,7 @@ shipping API.
 
 ## CreateFulfillmentFn
 
-<GenerationInfo sourceFile="packages/core/src/config/fulfillment/fulfillment-handler.ts" sourceLine="34" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/fulfillment/fulfillment-handler.ts" sourceLine="33" packageName="@vendure/core" />
 
 The function called when creating a new Fulfillment
 
@@ -168,7 +168,7 @@ type CreateFulfillmentFn<T extends ConfigArgs> = (
 
 ## CreateFulfillmentResult
 
-<GenerationInfo sourceFile="packages/core/src/config/fulfillment/fulfillment-handler.ts" sourceLine="24" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/fulfillment/fulfillment-handler.ts" sourceLine="23" packageName="@vendure/core" />
 
 
 

+ 1 - 1
docs/docs/reference/typescript-api/import-export/import-export-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ImportExportOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="879" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="880" packageName="@vendure/core" />
 
 Options related to importing & exporting data.
 

+ 1 - 1
docs/docs/reference/typescript-api/job-queue/job-queue-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## JobQueueOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="903" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="904" packageName="@vendure/core" />
 
 Options related to the built-in job queue.
 

+ 1 - 1
docs/docs/reference/typescript-api/orders/order-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## OrderOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="481" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="482" packageName="@vendure/core" />
 
 
 

+ 1 - 1
docs/docs/reference/typescript-api/payment/payment-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## PaymentOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="824" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="825" packageName="@vendure/core" />
 
 Defines payment-related options in the <a href='/reference/typescript-api/configuration/vendure-config#vendureconfig'>VendureConfig</a>.
 

+ 1 - 1
docs/docs/reference/typescript-api/products-stock/catalog-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CatalogOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="673" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="674" packageName="@vendure/core" />
 
 Options related to products and collections.
 

+ 1 - 1
docs/docs/reference/typescript-api/promotions/promotion-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## PromotionOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="735" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="736" packageName="@vendure/core" />
 
 
 

+ 47 - 0
docs/docs/reference/typescript-api/service-helpers/entity-duplicator-service.md

@@ -0,0 +1,47 @@
+---
+title: "EntityDuplicatorService"
+isDefaultIndex: false
+generated: true
+---
+<!-- This file was generated from the Vendure source. Do not modify. Instead, re-run the "docs:build" script -->
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+## EntityDuplicatorService
+
+<GenerationInfo sourceFile="packages/core/src/service/helpers/entity-duplicator/entity-duplicator.service.ts" sourceLine="23" packageName="@vendure/core" since="2.2.0" />
+
+This service is used to duplicate entities using one of the configured
+<a href='/reference/typescript-api/configuration/entity-duplicator#entityduplicator'>EntityDuplicator</a> functions.
+
+```ts title="Signature"
+class EntityDuplicatorService {
+    constructor(configService: ConfigService, configArgService: ConfigArgService, connection: TransactionalConnection)
+    getEntityDuplicators(ctx: RequestContext) => EntityDuplicatorDefinition[];
+    duplicateEntity(ctx: RequestContext, input: DuplicateEntityInput) => Promise<DuplicateEntityResult>;
+}
+```
+
+<div className="members-wrapper">
+
+### constructor
+
+<MemberInfo kind="method" type={`(configService: ConfigService, configArgService: ConfigArgService, connection: <a href='/reference/typescript-api/data-access/transactional-connection#transactionalconnection'>TransactionalConnection</a>) => EntityDuplicatorService`}   />
+
+
+### getEntityDuplicators
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>) => EntityDuplicatorDefinition[]`}   />
+
+Returns all configured <a href='/reference/typescript-api/configuration/entity-duplicator#entityduplicator'>EntityDuplicator</a> definitions.
+### duplicateEntity
+
+<MemberInfo kind="method" type={`(ctx: <a href='/reference/typescript-api/request/request-context#requestcontext'>RequestContext</a>, input: DuplicateEntityInput) => Promise&#60;DuplicateEntityResult&#62;`}   />
+
+Duplicates an entity using the specified <a href='/reference/typescript-api/configuration/entity-duplicator#entityduplicator'>EntityDuplicator</a>. The duplication is performed
+within a transaction, so if an error occurs, the transaction will be rolled back.
+
+
+</div>

+ 1 - 1
docs/docs/reference/typescript-api/shipping/shipping-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ShippingOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="751" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="752" packageName="@vendure/core" />
 
 
 

+ 1 - 1
docs/docs/reference/typescript-api/tax/tax-options.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## TaxOptions
 
-<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="856" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/vendure-config.ts" sourceLine="857" packageName="@vendure/core" />
 
 
 

+ 7 - 0
packages/core/src/api/schema/admin-api/duplicate-entity.api.graphql

@@ -1,8 +1,15 @@
 extend type Query {
+    """
+    Returns all configured EntityDuplicators.
+    """
     entityDuplicators: [EntityDuplicatorDefinition!]!
 }
 
 extend type Mutation {
+    """
+    Duplicate an existing entity using a specific EntityDuplicator.
+    Since v2.2.0.
+    """
     duplicateEntity(input: DuplicateEntityInput!): DuplicateEntityResult!
 }
 

+ 0 - 23
packages/core/src/config/entity/entity-duplication-strategy.ts

@@ -1,23 +0,0 @@
-import { Permission } from '@vendure/common/lib/generated-types';
-import { ID } from '@vendure/common/lib/shared-types';
-
-import { RequestContext } from '../../api/index';
-import { ConfigArgs, ConfigArgValues } from '../../common/configurable-operation';
-import { InjectableStrategy } from '../../common/index';
-import { VendureEntity } from '../../entity/index';
-
-export type InputArgValues<Strategy extends EntityDuplicationStrategy> = ConfigArgValues<
-    ReturnType<Strategy['defineInputArgs']>
->;
-
-export interface EntityDuplicationStrategy extends InjectableStrategy {
-    readonly requiresPermission: Array<Permission | string> | Permission | string;
-    readonly canDuplicateEntities: string[];
-    defineInputArgs(): ConfigArgs;
-    duplicate(input: {
-        ctx: RequestContext;
-        entityName: string;
-        id: ID;
-        args: InputArgValues<EntityDuplicationStrategy>;
-    }): Promise<VendureEntity>;
-}

+ 131 - 1
packages/core/src/config/entity/entity-duplicator.ts

@@ -10,6 +10,14 @@ import {
 } from '../../common/configurable-operation';
 import { VendureEntity } from '../../entity/index';
 
+/**
+ * @description
+ * A function which performs the duplication of an entity.
+ *
+ * @docsPage EntityDuplicator
+ * @docsCategory configuration
+ * @since 2.2.0
+ */
 export type DuplicateEntityFn<T extends ConfigArgs> = (input: {
     ctx: RequestContext;
     entityName: string;
@@ -19,28 +27,150 @@ export type DuplicateEntityFn<T extends ConfigArgs> = (input: {
 
 /**
  * @description
+ * Configuration for creating a new EntityDuplicator.
  *
- *
+ * @docsPage EntityDuplicator
+ * @docsCategory configuration
+ * @since 2.2.0
  */
 export interface EntityDuplicatorConfig<T extends ConfigArgs> extends ConfigurableOperationDefOptions<T> {
+    /**
+     * @description
+     * The permissions required in order to execute this duplicator. If an array is passed,
+     * then the administrator must have at least one of the permissions in the array.
+     */
     requiresPermission: Array<Permission | string> | Permission | string;
+    /**
+     * @description
+     * The entities for which this duplicator is able to duplicate.
+     */
     forEntities: string[];
+    /**
+     * @description
+     * The function which performs the duplication.
+     *
+     * @example
+     * ```ts
+     * duplicate: async input => {
+     *   const { ctx, id, args } = input;
+     *
+     *   // perform the duplication logic here
+     *
+     *   return newEntity;
+     * }
+     * ```
+     */
     duplicate: DuplicateEntityFn<T>;
 }
 
+/**
+ * @description
+ * An EntityDuplicator is used to define the logic for duplicating entities when the `duplicateEntity` mutation is called.
+ * This allows you to add support for duplication of both core and custom entities.
+ *
+ * @example
+ * ```ts title=src/config/custom-collection-duplicator.ts
+ * import { Collection, LanguageCode, Permission
+ *   EntityDuplicator, TransactionalConnection, CollectionService } from '\@vendure/core';
+ *
+ * let collectionService: CollectionService;
+ * let connection: TransactionalConnection;
+ *
+ * // This is just an example - we already ship with a built-in duplicator for Collections.
+ * const customCollectionDuplicator = new EntityDuplicator({
+ *     code: 'custom-collection-duplicator',
+ *     description: [{ languageCode: LanguageCode.en, value: 'Custom collection duplicator' }],
+ *     args: {
+ *         throwError: {
+ *             type: 'boolean',
+ *             defaultValue: false,
+ *         },
+ *     },
+ *     forEntities: ['Collection'],
+ *     requiresPermission: [Permission.UpdateCollection],
+ *     init(injector) {
+ *         collectionService = injector.get(CollectionService);
+ *         connection = injector.get(TransactionalConnection);
+ *     },
+ *     duplicate: async input => {
+ *         const { ctx, id, args } = input;
+ *
+ *         const original = await connection.getEntityOrThrow(ctx, Collection, id, {
+ *             relations: {
+ *                 assets: true,
+ *                 featuredAsset: true,
+ *             },
+ *         });
+ *         const newCollection = await collectionService.create(ctx, {
+ *             isPrivate: original.isPrivate,
+ *             customFields: original.customFields,
+ *             assetIds: original.assets.map(a => a.id),
+ *             featuredAssetId: original.featuredAsset?.id,
+ *             parentId: original.parentId,
+ *             filters: original.filters.map(f => ({
+ *                 code: f.code,
+ *                 arguments: f.args,
+ *             })),
+ *             inheritFilters: original.inheritFilters,
+ *             translations: original.translations.map(t => ({
+ *                 languageCode: t.languageCode,
+ *                 name: `${t.name} (copy)`,
+ *                 slug: `${t.slug}-copy`,
+ *                 description: t.description,
+ *                 customFields: t.customFields,
+ *             })),
+ *         });
+ *
+ *         if (args.throwError) {
+ *             // If an error is thrown at any point during the duplication process, the entire
+ *             // transaction will get automatically rolled back, and the mutation will return
+ *             // an ErrorResponse containing the error message.
+ *             throw new Error('Dummy error');
+ *         }
+ *
+ *         return newCollection;
+ *     },
+ * });
+ * ```
+ *
+ * The duplicator then gets passed to your VendureConfig object:
+ *
+ * ```ts title=src/vendure-config.ts
+ * import { VendureConfig, defaultEntityDuplicators } from '\@vendure/core';
+ * import { customCollectionDuplicator } from './config/custom-collection-duplicator';
+ *
+ * export const config: VendureConfig = {
+ *    // ...
+ *    entityOptions: {
+ *      entityDuplicators: [
+ *          ...defaultEntityDuplicators,
+ *          customCollectionDuplicator,
+ *      ],
+ *    },
+ * };
+ * ```
+ *
+ * @docsPage EntityDuplicator
+ * @docsWeight 0
+ * @docsCategory configuration
+ * @since 2.2.0
+ */
 export class EntityDuplicator<T extends ConfigArgs = ConfigArgs> extends ConfigurableOperationDef<T> {
     private _forEntities: string[];
     private _requiresPermission: Array<Permission | string> | Permission | string;
     private duplicateFn: DuplicateEntityFn<T>;
 
+    /** @internal */
     canDuplicate(entityName: string): boolean {
         return this._forEntities.includes(entityName);
     }
 
+    /** @internal */
     get forEntities() {
         return this._forEntities;
     }
 
+    /** @internal */
     get requiresPermission(): Permission[] {
         return (Array.isArray(this._requiresPermission)
             ? this._requiresPermission

+ 9 - 2
packages/core/src/config/vendure-config.ts

@@ -24,7 +24,6 @@ import { ProductVariantPriceUpdateStrategy } from './catalog/product-variant-pri
 import { StockDisplayStrategy } from './catalog/stock-display-strategy';
 import { StockLocationStrategy } from './catalog/stock-location-strategy';
 import { CustomFields } from './custom-field/custom-field-types';
-import { EntityDuplicationStrategy } from './entity/entity-duplication-strategy';
 import { EntityDuplicator } from './entity/entity-duplicator';
 import { EntityIdStrategy } from './entity/entity-id-strategy';
 import { MoneyStrategy } from './entity/money-strategy';
@@ -962,7 +961,15 @@ export interface EntityOptions {
      * @default AutoIncrementIdStrategy
      */
     entityIdStrategy?: EntityIdStrategy<any>;
-    entityDuplicators?: EntityDuplicator<any>[];
+    /**
+     * @description
+     * An array of {@link EntityDuplicator} instances which are used to duplicate entities
+     * when using the `duplicateEntity` mutation.
+     *
+     * @since 2.2.0
+     * @default defaultEntityDuplicators
+     */
+    entityDuplicators?: Array<EntityDuplicator<any>>;
     /**
      * @description
      * Defines the strategy used to store and round monetary values.

+ 17 - 0
packages/core/src/service/helpers/entity-duplicator/entity-duplicator.service.ts

@@ -12,6 +12,14 @@ import { ConfigService, Logger } from '../../../config/index';
 import { TransactionalConnection } from '../../../connection/index';
 import { ConfigArgService } from '../config-arg/config-arg.service';
 
+/**
+ * @description
+ * This service is used to duplicate entities using one of the configured
+ * {@link EntityDuplicator} functions.
+ *
+ * @docsCategory service-helpers
+ * @since 2.2.0
+ */
 @Injectable()
 export class EntityDuplicatorService {
     constructor(
@@ -20,6 +28,10 @@ export class EntityDuplicatorService {
         private connection: TransactionalConnection,
     ) {}
 
+    /**
+     * @description
+     * Returns all configured {@link EntityDuplicator} definitions.
+     */
     getEntityDuplicators(ctx: RequestContext): EntityDuplicatorDefinition[] {
         return this.configArgService.getDefinitions('EntityDuplicator').map(x => ({
             ...x.toGraphQlType(ctx),
@@ -29,6 +41,11 @@ export class EntityDuplicatorService {
         }));
     }
 
+    /**
+     * @description
+     * Duplicates an entity using the specified {@link EntityDuplicator}. The duplication is performed
+     * within a transaction, so if an error occurs, the transaction will be rolled back.
+     */
     async duplicateEntity(ctx: RequestContext, input: DuplicateEntityInput): Promise<DuplicateEntityResult> {
         const duplicator = this.configService.entityOptions.entityDuplicators.find(
             s => s.forEntities.includes(input.entityName) && s.code === input.duplicatorInput.code,