@unchainedshop/api 4.0.0-rc.5 → 4.0.0-rc.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +3 -1
  2. package/lib/errors.d.ts +2 -0
  3. package/lib/errors.d.ts.map +1 -1
  4. package/lib/errors.js +2 -0
  5. package/lib/errors.js.map +1 -1
  6. package/lib/loaders/index.d.ts +5 -0
  7. package/lib/loaders/index.d.ts.map +1 -1
  8. package/lib/loaders/index.js +2 -0
  9. package/lib/loaders/index.js.map +1 -1
  10. package/lib/loaders/productProxiesLoader.d.ts +10 -0
  11. package/lib/loaders/productProxiesLoader.d.ts.map +1 -0
  12. package/lib/loaders/productProxiesLoader.js +30 -0
  13. package/lib/loaders/productProxiesLoader.js.map +1 -0
  14. package/lib/mcp/tools/assortment/addAssortmentMediaUploadHandler.d.ts.map +1 -1
  15. package/lib/mcp/tools/assortment/addAssortmentMediaUploadHandler.js +2 -1
  16. package/lib/mcp/tools/assortment/addAssortmentMediaUploadHandler.js.map +1 -1
  17. package/lib/mcp/tools/assortment/assortmentMediaHandler.d.ts +32 -0
  18. package/lib/mcp/tools/assortment/assortmentMediaHandler.d.ts.map +1 -0
  19. package/lib/mcp/tools/assortment/assortmentMediaHandler.js +48 -0
  20. package/lib/mcp/tools/assortment/assortmentMediaHandler.js.map +1 -0
  21. package/lib/mcp/tools/assortment/index.d.ts.map +1 -1
  22. package/lib/mcp/tools/assortment/index.js +3 -1
  23. package/lib/mcp/tools/assortment/index.js.map +1 -1
  24. package/lib/mcp/tools/assortment/removeAssortmentMediaHandler.d.ts.map +1 -1
  25. package/lib/mcp/tools/assortment/removeAssortmentMediaHandler.js +3 -1
  26. package/lib/mcp/tools/assortment/removeAssortmentMediaHandler.js.map +1 -1
  27. package/lib/mcp/tools/assortment/reorderAssortmentMediaHandler.js +2 -2
  28. package/lib/mcp/tools/assortment/reorderAssortmentMediaHandler.js.map +1 -1
  29. package/lib/mcp/tools/assortment/translatedAssortmentMediaTextsHandler.d.ts.map +1 -1
  30. package/lib/mcp/tools/assortment/translatedAssortmentMediaTextsHandler.js +7 -1
  31. package/lib/mcp/tools/assortment/translatedAssortmentMediaTextsHandler.js.map +1 -1
  32. package/lib/mcp/tools/assortment/updateAssortmentMediaTextsHandler.d.ts.map +1 -1
  33. package/lib/mcp/tools/assortment/updateAssortmentMediaTextsHandler.js +4 -1
  34. package/lib/mcp/tools/assortment/updateAssortmentMediaTextsHandler.js.map +1 -1
  35. package/lib/mcp/tools/product/addProductAssignmentHandler.d.ts.map +1 -1
  36. package/lib/mcp/tools/product/addProductAssignmentHandler.js +1 -1
  37. package/lib/mcp/tools/product/addProductAssignmentHandler.js.map +1 -1
  38. package/lib/mcp/tools/product/addProductBundleItemHandler.d.ts +48 -0
  39. package/lib/mcp/tools/product/addProductBundleItemHandler.d.ts.map +1 -0
  40. package/lib/mcp/tools/product/addProductBundleItemHandler.js +52 -0
  41. package/lib/mcp/tools/product/addProductBundleItemHandler.js.map +1 -0
  42. package/lib/mcp/tools/product/addProductMediaUploadHandler.d.ts.map +1 -1
  43. package/lib/mcp/tools/product/addProductMediaUploadHandler.js +3 -2
  44. package/lib/mcp/tools/product/addProductMediaUploadHandler.js.map +1 -1
  45. package/lib/mcp/tools/product/catalogPriceHandler.d.ts +28 -0
  46. package/lib/mcp/tools/product/catalogPriceHandler.d.ts.map +1 -0
  47. package/lib/mcp/tools/product/catalogPriceHandler.js +57 -0
  48. package/lib/mcp/tools/product/catalogPriceHandler.js.map +1 -0
  49. package/lib/mcp/tools/product/index.d.ts.map +1 -1
  50. package/lib/mcp/tools/product/index.js +27 -3
  51. package/lib/mcp/tools/product/index.js.map +1 -1
  52. package/lib/mcp/tools/product/productAssignmentsHandler.d.ts +24 -0
  53. package/lib/mcp/tools/product/productAssignmentsHandler.d.ts.map +1 -0
  54. package/lib/mcp/tools/product/productAssignmentsHandler.js +79 -0
  55. package/lib/mcp/tools/product/productAssignmentsHandler.js.map +1 -0
  56. package/lib/mcp/tools/product/productBundleItemsHandler.d.ts +20 -0
  57. package/lib/mcp/tools/product/productBundleItemsHandler.d.ts.map +1 -0
  58. package/lib/mcp/tools/product/productBundleItemsHandler.js +45 -0
  59. package/lib/mcp/tools/product/productBundleItemsHandler.js.map +1 -0
  60. package/lib/mcp/tools/product/productMediaHandler.d.ts +32 -0
  61. package/lib/mcp/tools/product/productMediaHandler.d.ts.map +1 -0
  62. package/lib/mcp/tools/product/productMediaHandler.js +46 -0
  63. package/lib/mcp/tools/product/productMediaHandler.js.map +1 -0
  64. package/lib/mcp/tools/product/productSiblingsHandler.d.ts +36 -0
  65. package/lib/mcp/tools/product/productSiblingsHandler.d.ts.map +1 -0
  66. package/lib/mcp/tools/product/productSiblingsHandler.js +66 -0
  67. package/lib/mcp/tools/product/productSiblingsHandler.js.map +1 -0
  68. package/lib/mcp/tools/product/productVariationsHandler.d.ts +20 -0
  69. package/lib/mcp/tools/product/productVariationsHandler.d.ts.map +1 -0
  70. package/lib/mcp/tools/product/productVariationsHandler.js +47 -0
  71. package/lib/mcp/tools/product/productVariationsHandler.js.map +1 -0
  72. package/lib/mcp/tools/product/removeProductBundleItemHandler.d.ts +24 -0
  73. package/lib/mcp/tools/product/removeProductBundleItemHandler.d.ts.map +1 -0
  74. package/lib/mcp/tools/product/removeProductBundleItemHandler.js +46 -0
  75. package/lib/mcp/tools/product/removeProductBundleItemHandler.js.map +1 -0
  76. package/lib/mcp/tools/product/removeProductMediaHandler.js +1 -1
  77. package/lib/mcp/tools/product/removeProductMediaHandler.js.map +1 -1
  78. package/lib/mcp/tools/product/reorderProductMediaHandler.js +2 -2
  79. package/lib/mcp/tools/product/reorderProductMediaHandler.js.map +1 -1
  80. package/lib/mcp/tools/product/simulatedPriceHandler.d.ts +70 -0
  81. package/lib/mcp/tools/product/simulatedPriceHandler.d.ts.map +1 -0
  82. package/lib/mcp/tools/product/simulatedPriceHandler.js +74 -0
  83. package/lib/mcp/tools/product/simulatedPriceHandler.js.map +1 -0
  84. package/lib/mcp/tools/product/simulatedPriceRangeHandler.d.ts +74 -0
  85. package/lib/mcp/tools/product/simulatedPriceRangeHandler.d.ts.map +1 -0
  86. package/lib/mcp/tools/product/simulatedPriceRangeHandler.js +103 -0
  87. package/lib/mcp/tools/product/simulatedPriceRangeHandler.js.map +1 -0
  88. package/lib/mcp/tools/product/translatedProductMediaTextsHandler.d.ts.map +1 -1
  89. package/lib/mcp/tools/product/translatedProductMediaTextsHandler.js +7 -1
  90. package/lib/mcp/tools/product/translatedProductMediaTextsHandler.js.map +1 -1
  91. package/lib/mcp/tools/product/translatedProductVariationTextsHandler.d.ts.map +1 -1
  92. package/lib/mcp/tools/product/translatedProductVariationTextsHandler.js +7 -1
  93. package/lib/mcp/tools/product/translatedProductVariationTextsHandler.js.map +1 -1
  94. package/lib/mcp/tools/product/updateProductMediaTextsHandler.js +1 -1
  95. package/lib/mcp/tools/product/updateProductMediaTextsHandler.js.map +1 -1
  96. package/lib/mcp/tools/product/updateProductPlanHandler.d.ts +47 -26
  97. package/lib/mcp/tools/product/updateProductPlanHandler.d.ts.map +1 -1
  98. package/lib/mcp/tools/product/updateProductPlanHandler.js +23 -7
  99. package/lib/mcp/tools/product/updateProductPlanHandler.js.map +1 -1
  100. package/lib/mcp/tools/product/updateProductVariationTextsHandler.d.ts +73 -0
  101. package/lib/mcp/tools/product/updateProductVariationTextsHandler.d.ts.map +1 -0
  102. package/lib/mcp/tools/product/updateProductVariationTextsHandler.js +52 -0
  103. package/lib/mcp/tools/product/updateProductVariationTextsHandler.js.map +1 -0
  104. package/lib/mcp/tools/product/variationProductsHandler.d.ts +52 -0
  105. package/lib/mcp/tools/product/variationProductsHandler.d.ts.map +1 -0
  106. package/lib/mcp/tools/product/variationProductsHandler.js +54 -0
  107. package/lib/mcp/tools/product/variationProductsHandler.js.map +1 -0
  108. package/lib/mcp/tools/shopInfoHandler.d.ts.map +1 -1
  109. package/lib/mcp/tools/shopInfoHandler.js +4 -2
  110. package/lib/mcp/tools/shopInfoHandler.js.map +1 -1
  111. package/lib/mcp/utils/getNormalizedProductDetails.d.ts +1 -1
  112. package/lib/mcp/utils/getNormalizedProductDetails.d.ts.map +1 -1
  113. package/lib/mcp/utils/getNormalizedProductDetails.js +25 -0
  114. package/lib/mcp/utils/getNormalizedProductDetails.js.map +1 -1
  115. package/lib/resolvers/mutations/filters/createFilter.d.ts.map +1 -1
  116. package/lib/resolvers/mutations/filters/createFilter.js +14 -5
  117. package/lib/resolvers/mutations/filters/createFilter.js.map +1 -1
  118. package/lib/resolvers/mutations/products/createProductBundleItem.d.ts.map +1 -1
  119. package/lib/resolvers/mutations/products/createProductBundleItem.js +7 -2
  120. package/lib/resolvers/mutations/products/createProductBundleItem.js.map +1 -1
  121. package/lib/resolvers/type/index.d.ts +9 -0
  122. package/lib/resolvers/type/index.d.ts.map +1 -1
  123. package/lib/resolvers/type/index.js +2 -0
  124. package/lib/resolvers/type/index.js.map +1 -1
  125. package/lib/resolvers/type/product/product-bundle-types.d.ts +1 -0
  126. package/lib/resolvers/type/product/product-bundle-types.d.ts.map +1 -1
  127. package/lib/resolvers/type/product/product-configurable-or-bundle-product-types.d.ts +5 -0
  128. package/lib/resolvers/type/product/product-configurable-or-bundle-product-types.d.ts.map +1 -0
  129. package/lib/resolvers/type/product/product-configurable-or-bundle-product-types.js +9 -0
  130. package/lib/resolvers/type/product/product-configurable-or-bundle-product-types.js.map +1 -0
  131. package/lib/resolvers/type/product/product-configurable-types.d.ts +1 -0
  132. package/lib/resolvers/type/product/product-configurable-types.d.ts.map +1 -1
  133. package/lib/resolvers/type/product/product-plan-types.d.ts +1 -0
  134. package/lib/resolvers/type/product/product-plan-types.d.ts.map +1 -1
  135. package/lib/resolvers/type/product/product-simple-types.d.ts +1 -0
  136. package/lib/resolvers/type/product/product-simple-types.d.ts.map +1 -1
  137. package/lib/resolvers/type/product/product-tokenized-types.d.ts +1 -0
  138. package/lib/resolvers/type/product/product-tokenized-types.d.ts.map +1 -1
  139. package/lib/resolvers/type/product/product-types.d.ts +1 -0
  140. package/lib/resolvers/type/product/product-types.d.ts.map +1 -1
  141. package/lib/resolvers/type/product/product-types.js +3 -0
  142. package/lib/resolvers/type/product/product-types.js.map +1 -1
  143. package/lib/schema/types/product/bundle-product.d.ts.map +1 -1
  144. package/lib/schema/types/product/bundle-product.js +2 -1
  145. package/lib/schema/types/product/bundle-product.js.map +1 -1
  146. package/lib/schema/types/product/configurable-product.d.ts.map +1 -1
  147. package/lib/schema/types/product/configurable-product.js +1 -0
  148. package/lib/schema/types/product/configurable-product.js.map +1 -1
  149. package/lib/schema/types/product/plan-product.d.ts.map +1 -1
  150. package/lib/schema/types/product/plan-product.js +1 -0
  151. package/lib/schema/types/product/plan-product.js.map +1 -1
  152. package/lib/schema/types/product/product.d.ts.map +1 -1
  153. package/lib/schema/types/product/product.js +3 -0
  154. package/lib/schema/types/product/product.js.map +1 -1
  155. package/lib/schema/types/product/simple-product.d.ts.map +1 -1
  156. package/lib/schema/types/product/simple-product.js +1 -0
  157. package/lib/schema/types/product/simple-product.js.map +1 -1
  158. package/lib/schema/types/product/tokenized-product.d.ts.map +1 -1
  159. package/lib/schema/types/product/tokenized-product.js +1 -0
  160. package/lib/schema/types/product/tokenized-product.js.map +1 -1
  161. package/package.json +6 -6
  162. package/src/errors.ts +10 -0
  163. package/src/loaders/index.ts +2 -0
  164. package/src/loaders/productProxiesLoader.ts +37 -0
  165. package/src/mcp/tools/assortment/addAssortmentMediaUploadHandler.ts +2 -1
  166. package/src/mcp/tools/assortment/assortmentMediaHandler.ts +57 -0
  167. package/src/mcp/tools/assortment/index.ts +9 -1
  168. package/src/mcp/tools/assortment/removeAssortmentMediaHandler.ts +3 -1
  169. package/src/mcp/tools/assortment/reorderAssortmentMediaHandler.ts +2 -2
  170. package/src/mcp/tools/assortment/translatedAssortmentMediaTextsHandler.ts +8 -1
  171. package/src/mcp/tools/assortment/updateAssortmentMediaTextsHandler.ts +4 -1
  172. package/src/mcp/tools/product/addProductAssignmentHandler.ts +3 -1
  173. package/src/mcp/tools/product/addProductBundleItemHandler.ts +64 -0
  174. package/src/mcp/tools/product/addProductMediaUploadHandler.ts +3 -2
  175. package/src/mcp/tools/product/catalogPriceHandler.ts +61 -0
  176. package/src/mcp/tools/product/index.ts +104 -3
  177. package/src/mcp/tools/product/productAssignmentsHandler.ts +86 -0
  178. package/src/mcp/tools/product/productBundleItemsHandler.ts +50 -0
  179. package/src/mcp/tools/product/productMediaHandler.ts +53 -0
  180. package/src/mcp/tools/product/productSiblingsHandler.ts +82 -0
  181. package/src/mcp/tools/product/productVariationsHandler.ts +53 -0
  182. package/src/mcp/tools/product/removeProductBundleItemHandler.ts +55 -0
  183. package/src/mcp/tools/product/removeProductMediaHandler.ts +1 -1
  184. package/src/mcp/tools/product/reorderProductMediaHandler.ts +2 -2
  185. package/src/mcp/tools/product/simulatedPriceHandler.ts +82 -0
  186. package/src/mcp/tools/product/simulatedPriceRangeHandler.ts +126 -0
  187. package/src/mcp/tools/product/translatedProductMediaTextsHandler.ts +6 -2
  188. package/src/mcp/tools/product/translatedProductVariationTextsHandler.ts +6 -1
  189. package/src/mcp/tools/product/updateProductMediaTextsHandler.ts +1 -1
  190. package/src/mcp/tools/product/updateProductPlanHandler.ts +37 -7
  191. package/src/mcp/tools/product/updateProductVariationTextsHandler.ts +65 -0
  192. package/src/mcp/tools/product/variationProductsHandler.ts +61 -0
  193. package/src/mcp/tools/shopInfoHandler.ts +4 -2
  194. package/src/mcp/utils/getNormalizedProductDetails.ts +28 -0
  195. package/src/resolvers/mutations/filters/createFilter.ts +11 -6
  196. package/src/resolvers/mutations/products/createProductBundleItem.ts +13 -3
  197. package/src/resolvers/type/index.ts +2 -0
  198. package/src/resolvers/type/product/product-configurable-or-bundle-product-types.ts +10 -0
  199. package/src/resolvers/type/product/product-types.ts +4 -0
  200. package/src/schema/types/product/bundle-product.ts +2 -1
  201. package/src/schema/types/product/configurable-product.ts +1 -0
  202. package/src/schema/types/product/plan-product.ts +1 -0
  203. package/src/schema/types/product/product.ts +3 -0
  204. package/src/schema/types/product/simple-product.ts +1 -0
  205. package/src/schema/types/product/tokenized-product.ts +1 -0
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod';
2
+ import { Context } from '../../../context.js';
3
+ import { log } from '@unchainedshop/logger';
4
+ import { ProductVariationNotFoundError } from '../../../errors.js';
5
+
6
+ export const ProductVariationTextInputSchema = z.object({
7
+ locale: z.string().min(1).describe('Locale code (e.g., "en", "de")'),
8
+ title: z.string().optional().describe('Title of the variation in the given locale'),
9
+ subtitle: z.string().optional().describe('Subtitle of the variation in the given locale'),
10
+ });
11
+
12
+ export const UpdateProductVariationTextsSchema = {
13
+ productVariationId: z.string().min(1).describe('ID of the product variation to update'),
14
+ productVariationOptionValue: z
15
+ .string()
16
+ .optional()
17
+ .describe('Optional production option value to filter variations'),
18
+ texts: z
19
+ .array(ProductVariationTextInputSchema)
20
+ .min(1)
21
+ .describe('Localized texts to apply to the variation'),
22
+ };
23
+
24
+ export const UpdateProductVariationTextsZodSchema = z.object(UpdateProductVariationTextsSchema);
25
+
26
+ export type UpdateProductVariationTextsParams = z.infer<typeof UpdateProductVariationTextsZodSchema>;
27
+
28
+ export async function updateProductVariationTextsHandler(
29
+ context: Context,
30
+ params: UpdateProductVariationTextsParams,
31
+ ) {
32
+ const { productVariationId, productVariationOptionValue, texts } = params;
33
+ const { modules, userId } = context;
34
+
35
+ try {
36
+ log('handler updateProductVariationTextsHandler', { userId, params });
37
+ const productVariation = await modules.products.variations.findProductVariation({
38
+ productVariationId,
39
+ });
40
+ if (!productVariation) throw new ProductVariationNotFoundError({ productVariationId });
41
+
42
+ const updatedTexts = await modules.products.variations.texts.updateVariationTexts(
43
+ productVariationId,
44
+ texts as any,
45
+ productVariationOptionValue,
46
+ );
47
+ return {
48
+ content: [
49
+ {
50
+ type: 'text' as const,
51
+ text: JSON.stringify({ texts: updatedTexts, productId: productVariation.productId }),
52
+ },
53
+ ],
54
+ };
55
+ } catch (error) {
56
+ return {
57
+ content: [
58
+ {
59
+ type: 'text' as const,
60
+ text: `Error updating product warehousing: ${(error as Error).message}`,
61
+ },
62
+ ],
63
+ };
64
+ }
65
+ }
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod';
2
+ import { Context } from '../../../context.js';
3
+ import { ProductNotFoundError, ProductWrongTypeError } from '../../../errors.js';
4
+ import { log } from '@unchainedshop/logger';
5
+ import { getNormalizedProductDetails } from '../../utils/getNormalizedProductDetails.js';
6
+
7
+ const VariationProductsVectorSchema = z.object({
8
+ key: z.string().min(1),
9
+ value: z.string().min(1),
10
+ });
11
+
12
+ export const VariationProductsSchema = {
13
+ productId: z.string().min(1),
14
+ vectors: z.array(VariationProductsVectorSchema).min(1),
15
+ includeInactive: z.boolean().optional().default(true).describe('Whether to include inactive products'),
16
+ };
17
+ export const VariationProductsZodSchema = z.object(VariationProductsSchema);
18
+ export type VariationProductsParams = z.infer<typeof VariationProductsZodSchema>;
19
+
20
+ export const variationProductsHandler = async (context: Context, params: VariationProductsParams) => {
21
+ const { modules, userId } = context;
22
+ const { productId, vectors, includeInactive } = params;
23
+
24
+ try {
25
+ log('handler variationProductsHandler', { userId, params });
26
+ const product = await modules.products.findProduct({ productId });
27
+ if (!product) throw new ProductNotFoundError({ productId });
28
+
29
+ if (product.type !== 'CONFIGURABLE_PRODUCT') {
30
+ throw new ProductWrongTypeError({
31
+ productId,
32
+ received: product.type,
33
+ required: 'CONFIGURABLE_PRODUCT',
34
+ });
35
+ }
36
+
37
+ const products = await modules.products.proxyProducts(product, vectors as any, {
38
+ includeInactive,
39
+ });
40
+ const normalizedProducts = await Promise.all(
41
+ products.map(async ({ _id }) => getNormalizedProductDetails(_id, context)),
42
+ );
43
+ return {
44
+ content: [
45
+ {
46
+ type: 'text' as const,
47
+ text: JSON.stringify({ products: normalizedProducts }),
48
+ },
49
+ ],
50
+ };
51
+ } catch (error) {
52
+ return {
53
+ content: [
54
+ {
55
+ type: 'text' as const,
56
+ text: `Error fetching variation products: ${(error as Error).message}`,
57
+ },
58
+ ],
59
+ };
60
+ }
61
+ };
@@ -5,12 +5,14 @@ export async function shopInfoHandler(context: Context) {
5
5
  const { version, userId, loaders, locale, countryCode } = context;
6
6
 
7
7
  try {
8
- log('query shopInfo', { userId });
8
+ log('handler shopInfoHandler', { userId });
9
9
 
10
10
  const language = await loaders.languageLoader.load({ isoCode: locale.language });
11
11
 
12
12
  const country = await loaders.countryLoader.load({ isoCode: countryCode });
13
- const defaultLanguageIsoCode = language.isoCode + '-' + country.isoCode;
13
+ const defaultLanguageIsoCode = language?.isoCode
14
+ ? `${language.isoCode}${country?.isoCode ? '-' + country.isoCode : ''}`
15
+ : null;
14
16
 
15
17
  return {
16
18
  content: [
@@ -13,11 +13,38 @@ export async function getNormalizedProductDetails(productId: string, context: Co
13
13
  locale,
14
14
  });
15
15
  let variations = [];
16
+ let bundleItems = [];
16
17
  if (product.type === ProductTypes.ConfigurableProduct)
17
18
  variations = await context.modules.products.variations.findProductVariations({
18
19
  productId: product._id,
19
20
  });
20
21
 
22
+ if (product.type === ProductTypes.BundleProduct) {
23
+ bundleItems = await Promise.all(
24
+ (product.bundleItems || []).map(async (item) => {
25
+ const bundleItemProduct = await loaders.productLoader.load({
26
+ productId: item.productId,
27
+ });
28
+ const productMedias = await modules.products.media.findProductMedias({
29
+ productId: item.productId,
30
+ });
31
+ const media = await normalizeMediaUrl(productMedias, context);
32
+ const texts = await loaders.productTextLoader.load({
33
+ productId: item.productId,
34
+ locale,
35
+ });
36
+ return {
37
+ product: {
38
+ ...bundleItemProduct,
39
+ media,
40
+ texts,
41
+ },
42
+ ...item,
43
+ };
44
+ }),
45
+ );
46
+ }
47
+
21
48
  // Get pricing information
22
49
  let pricing = null;
23
50
  try {
@@ -37,6 +64,7 @@ export async function getNormalizedProductDetails(productId: string, context: Co
37
64
  texts,
38
65
  media,
39
66
  variations,
67
+ bundleItems,
40
68
  pricing,
41
69
  };
42
70
  }
@@ -2,6 +2,7 @@ import { log } from '@unchainedshop/logger';
2
2
  import { Filter } from '@unchainedshop/core-filters';
3
3
  import { Context } from '../../../context.js';
4
4
  import { FilterDirector, FilterInputText } from '@unchainedshop/core';
5
+ import { DuplicateFilterKeyError } from '../../../errors.js';
5
6
 
6
7
  export default async function createFilter(
7
8
  root: never,
@@ -10,13 +11,17 @@ export default async function createFilter(
10
11
  ) {
11
12
  const { modules, userId } = context;
12
13
  log('mutation createFilter', { userId });
14
+ try {
15
+ const newFilter = await modules.filters.create(filter);
13
16
 
14
- const newFilter = await modules.filters.create(filter);
17
+ await FilterDirector.invalidateProductIdCache(newFilter, context);
15
18
 
16
- await FilterDirector.invalidateProductIdCache(newFilter, context);
17
-
18
- if (texts) {
19
- await modules.filters.texts.updateTexts({ filterId: newFilter._id }, texts);
19
+ if (texts) {
20
+ await modules.filters.texts.updateTexts({ filterId: newFilter._id }, texts);
21
+ }
22
+ return newFilter;
23
+ } catch (e) {
24
+ if (e?.message?.includes('duplicate')) throw new DuplicateFilterKeyError({ filter });
25
+ else throw e;
20
26
  }
21
- return newFilter;
22
27
  }
@@ -2,7 +2,12 @@ import { log } from '@unchainedshop/logger';
2
2
  import { ProductTypes } from '@unchainedshop/core-products';
3
3
  import { Context } from '../../../context.js';
4
4
  import { ProductBundleItem } from '@unchainedshop/core-products';
5
- import { ProductNotFoundError, InvalidIdError, ProductWrongTypeError } from '../../../errors.js';
5
+ import {
6
+ ProductNotFoundError,
7
+ InvalidIdError,
8
+ ProductWrongTypeError,
9
+ CyclicProductBundlingNotSupportedError,
10
+ } from '../../../errors.js';
6
11
 
7
12
  export default async function createProductBundleItem(
8
13
  root: never,
@@ -24,8 +29,13 @@ export default async function createProductBundleItem(
24
29
  required: ProductTypes.BundleProduct,
25
30
  });
26
31
 
27
- if (!(await modules.products.productExists({ productId: item.productId })))
28
- throw new ProductNotFoundError({ productId: item.productId });
32
+ const itemProduct = await modules.products.findProduct({ productId: item.productId });
33
+ if (!itemProduct) throw new ProductNotFoundError({ productId: item.productId });
34
+
35
+ if (itemProduct._id === product._id)
36
+ throw new CyclicProductBundlingNotSupportedError({
37
+ productId: itemProduct._id,
38
+ });
29
39
 
30
40
  await modules.products.bundleItems.addBundleItem(productId, item);
31
41
 
@@ -52,6 +52,7 @@ import { ProductVariation } from './product/product-variation-types.js';
52
52
  import { ProductVariationAssignment } from './product/product-variation-assignment-types.js';
53
53
  import { ProductVariationAssignmentVector } from './product/product-variation-assignment-vector.js';
54
54
  import { ProductVariationOption } from './product/product-variation-option-types.js';
55
+ import { ConfigurableOrBundleProduct } from './product/product-configurable-or-bundle-product-types.js';
55
56
  import { Quotation } from './quotation-types.js';
56
57
  import { Shop } from './shop-types.js';
57
58
  import { SimpleProduct } from './product/product-simple-types.js';
@@ -123,6 +124,7 @@ const types = {
123
124
  ProductVariationAssignmentVector,
124
125
  ProductVariationOption,
125
126
  ProductSearchResult,
127
+ ConfigurableOrBundleProduct,
126
128
  Quotation,
127
129
  Shop,
128
130
  SimpleProduct,
@@ -0,0 +1,10 @@
1
+ import { Product as ProductType } from '@unchainedshop/core-products';
2
+ import { ProductTypes } from '@unchainedshop/core-products';
3
+ import { objectInvert } from '@unchainedshop/utils';
4
+
5
+ export const ConfigurableOrBundleProduct = {
6
+ __resolveType: (product: ProductType): string => {
7
+ const invertedProductTypes = objectInvert(ProductTypes);
8
+ return invertedProductTypes[product.type];
9
+ },
10
+ };
@@ -13,6 +13,10 @@ export const Product = {
13
13
  return invertedProductTypes[product.type];
14
14
  },
15
15
 
16
+ async proxies(product: ProductType, _: never, context: Context): Promise<ProductType[]> {
17
+ return context.loaders.productProxiesLoader.load({ productId: product._id });
18
+ },
19
+
16
20
  async assortmentPaths(
17
21
  product: ProductType,
18
22
  params: {
@@ -1,7 +1,7 @@
1
1
  export default [
2
2
  /* GraphQL */ `
3
3
  """
4
- A Bundle product consists of multiple configured products
4
+ A Bundle product consists of multiple products
5
5
  """
6
6
  type BundleProduct implements Product @cacheControl(maxAge: 180) {
7
7
  _id: ID!
@@ -22,6 +22,7 @@ export default [
22
22
  ): [ProductReview!]!
23
23
  reviewsCount(queryString: String): Int!
24
24
  assortmentPaths: [ProductAssortmentPath!]!
25
+ proxies: [ConfigurableOrBundleProduct!]!
25
26
  siblings(
26
27
  assortmentId: ID
27
28
  limit: Int = 10
@@ -73,6 +73,7 @@ export default [
73
73
  media(limit: Int = 10, offset: Int = 0, tags: [LowerCaseString!]): [ProductMedia!]!
74
74
  texts(forceLocale: Locale): ProductTexts
75
75
  assortmentPaths: [ProductAssortmentPath!]!
76
+ proxies: [ConfigurableOrBundleProduct!]!
76
77
  siblings(
77
78
  assortmentId: ID
78
79
  limit: Int = 10
@@ -51,6 +51,7 @@ export default [
51
51
  configuration: [ProductConfigurationParameterInput!]
52
52
  ): Price @cacheControl(scope: PRIVATE)
53
53
  assortmentPaths(forceLocale: Locale): [ProductAssortmentPath!]!
54
+ proxies: [ConfigurableOrBundleProduct!]!
54
55
  siblings(
55
56
  assortmentId: ID
56
57
  limit: Int = 10
@@ -71,6 +71,7 @@ export default [
71
71
  ): [ProductReview!]!
72
72
  reviewsCount(queryString: String): Int!
73
73
  assortmentPaths: [ProductAssortmentPath!]!
74
+ proxies: [ConfigurableOrBundleProduct!]!
74
75
  siblings(
75
76
  assortmentId: ID
76
77
  limit: Int = 10
@@ -132,5 +133,7 @@ export default [
132
133
  amount: Int!
133
134
  maxQuantity: Int
134
135
  }
136
+
137
+ union ConfigurableOrBundleProduct = BundleProduct | ConfigurableProduct
135
138
  `,
136
139
  ];
@@ -31,6 +31,7 @@ export default [
31
31
  referenceDate: Timestamp
32
32
  ): [Stock!] @cacheControl(scope: PRIVATE, maxAge: 10)
33
33
  assortmentPaths: [ProductAssortmentPath!]!
34
+ proxies: [ConfigurableOrBundleProduct!]!
34
35
  siblings(
35
36
  assortmentId: ID
36
37
  limit: Int = 10
@@ -42,6 +42,7 @@ export default [
42
42
  ): Price @cacheControl(scope: PRIVATE, maxAge: 10)
43
43
  simulatedStocks(referenceDate: Timestamp): [Stock!] @cacheControl(scope: PRIVATE, maxAge: 10)
44
44
  assortmentPaths(forceLocale: Locale): [ProductAssortmentPath!]!
45
+ proxies: [ConfigurableOrBundleProduct!]!
45
46
  siblings(
46
47
  assortmentId: ID
47
48
  limit: Int = 10