From e8bc6208cf9580f3606da8dc5a9fd596e5d79c8a Mon Sep 17 00:00:00 2001 From: Mikael Araya Date: Mon, 6 Jan 2025 23:25:24 +0300 Subject: [PATCH 1/4] Fix variation assignement unique identifier and removeAssignment operation --- package-lock.json | 262 +++++++++++++++++- .../products/removeProductAssignment.ts | 6 +- .../product-variation-assignment-types.ts | 2 +- packages/api/src/schema/mutation.ts | 6 +- .../src/module/configureProductsModule.ts | 10 +- packages/types/products.ts | 4 +- 6 files changed, 268 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index c4711569f6..22eb08d6f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2245,6 +2245,139 @@ "node": ">= 16.20.1" } }, + "node_modules/@mongodb-js/zstd-darwin-arm64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-darwin-arm64/-/zstd-darwin-arm64-1.2.2.tgz", + "integrity": "sha512-hjQgub8fhn3itewwRSCSe3sl8rmnbZOFwuBHOZj4j4xu1Hde7xs+ACkfeEvvmNjUuxAzvc1MnDjHX2UwUlA7qA==", + "cpu": [ + "arm64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-darwin-x64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-darwin-x64/-/zstd-darwin-x64-1.2.2.tgz", + "integrity": "sha512-gHSxcWgAdED/bDKyOqLhiwC0VYlhkhSyQuR0Fqcrl1CMpWSJAfu0jxhaEvM4Ncs6yH0+BPVwTp8xtHW2iJ5DuQ==", + "cpu": [ + "x64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-linux-arm64-gnu": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-linux-arm64-gnu/-/zstd-linux-arm64-gnu-1.2.2.tgz", + "integrity": "sha512-DJVSC5IU/GlY9lY4qpKML/655OstZvueN/WZeuO8hyYJ5115x/usUM1pHpp7dbZWyUUhTlRIU4peR62Tk1H3lQ==", + "cpu": [ + "arm64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-linux-arm64-musl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-linux-arm64-musl/-/zstd-linux-arm64-musl-1.2.2.tgz", + "integrity": "sha512-rxTmMF2NGLLijnw+MZCs0ixiE+NylpDYvUUkJemqjyHstlmYG0Ku6i3+SzViB6L0kkktwYSRpaI4y7OG+SFt3w==", + "cpu": [ + "arm64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-linux-x64-gnu": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-linux-x64-gnu/-/zstd-linux-x64-gnu-1.2.2.tgz", + "integrity": "sha512-ctz/XY6aX2THxkTjAygOYbyFp2/UYbqZIF3sB1F5Cjcbus9wfD3vPbaDegqRjQhlHBvKia3IjqJDUiC+aZxmww==", + "cpu": [ + "x64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-linux-x64-musl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-linux-x64-musl/-/zstd-linux-x64-musl-1.2.2.tgz", + "integrity": "sha512-EqD271yPIRBGbkMtBJSVTLlUrukTsmi7qt1G3dh+Z9RWVXn5MXnlnEn4znfBXoaVqlniUNZhmzDEaATNIktJpw==", + "cpu": [ + "x64" + ], + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@mongodb-js/zstd-win32-x64-msvc": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd-win32-x64-msvc/-/zstd-win32-x64-msvc-1.2.2.tgz", + "integrity": "sha512-h3O9NGOrGtfQ7g+rFqs5Hn+GSmgF2ik+xg0/1crNUXWthcBsxcCK5woawHfDcJxWkaiijJe5PVo6/fo8Jm7qCg==", + "cpu": [ + "x64" + ], + "deprecated": "This package is no longer maintained. Please use @mongodb-js/zstd@2.x instead.", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -7769,7 +7902,7 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-4.3.3.tgz", "integrity": "sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "abort-controller": "^3.0.0", @@ -7786,7 +7919,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -7799,7 +7932,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -7810,17 +7943,64 @@ } }, "node_modules/gcp-metadata": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", - "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", - "devOptional": true, + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-5.3.0.tgz", + "integrity": "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { - "gaxios": "^4.0.0", + "gaxios": "^5.0.0", "json-bigint": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" + } + }, + "node_modules/gcp-metadata/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/gcp-metadata/node_modules/gaxios": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.1.3.tgz", + "integrity": "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/gcp-metadata/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, "node_modules/generic-pool": { @@ -8328,6 +8508,20 @@ "node": ">=8" } }, + "node_modules/google-auth-library/node_modules/gcp-metadata": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/google-auth-library/node_modules/jwa": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", @@ -8430,6 +8624,20 @@ "node": ">=8" } }, + "node_modules/googleapis-common/node_modules/gcp-metadata": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/googleapis-common/node_modules/google-auth-library": { "version": "7.14.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", @@ -8514,6 +8722,20 @@ "node": ">=8" } }, + "node_modules/googleapis/node_modules/gcp-metadata": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-4.3.1.tgz", + "integrity": "sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^4.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/googleapis/node_modules/google-auth-library": { "version": "7.14.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.14.1.tgz", @@ -11743,6 +11965,28 @@ "dev": true, "license": "MIT" }, + "node_modules/mongodb-memory-server/node_modules/@mongodb-js/zstd": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/zstd/-/zstd-1.2.2.tgz", + "integrity": "sha512-NRXiFhk2Nl8UMuIZ4pviKkGVZY/e5P37Opam1u0OtgXjEE0kO1HLapA9heTcZ1PUomArnKS426XbiRFr5iaWvw==", + "deprecated": "1.x versions of this package are deprecated, please use 2.x instead", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@mongodb-js/zstd-darwin-arm64": "1.2.2", + "@mongodb-js/zstd-darwin-x64": "1.2.2", + "@mongodb-js/zstd-linux-arm64-gnu": "1.2.2", + "@mongodb-js/zstd-linux-arm64-musl": "1.2.2", + "@mongodb-js/zstd-linux-x64-gnu": "1.2.2", + "@mongodb-js/zstd-linux-x64-musl": "1.2.2", + "@mongodb-js/zstd-win32-x64-msvc": "1.2.2" + } + }, "node_modules/mongodb-memory-server/node_modules/@types/whatwg-url": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", diff --git a/packages/api/src/resolvers/mutations/products/removeProductAssignment.ts b/packages/api/src/resolvers/mutations/products/removeProductAssignment.ts index a14bc6eb2a..5459eaaa06 100644 --- a/packages/api/src/resolvers/mutations/products/removeProductAssignment.ts +++ b/packages/api/src/resolvers/mutations/products/removeProductAssignment.ts @@ -6,10 +6,10 @@ import { InvalidIdError, ProductNotFoundError, ProductWrongTypeError } from '../ export default async function removeProductAssignment( root: Root, - params: { proxyId: string; vectors: Array }, + params: { proxyId: string; vectors: Array; productId: string }, { modules, userId }: Context, ) { - const { proxyId, vectors } = params; + const { proxyId, vectors, productId } = params; log(`mutation removeProductAssignment ${proxyId}`, { userId }); if (!proxyId) throw new InvalidIdError({ proxyId }); @@ -24,7 +24,7 @@ export default async function removeProductAssignment( required: ProductTypes.ConfigurableProduct, }); - await modules.products.assignments.removeAssignment(proxyId, { vectors }); + await modules.products.assignments.removeAssignment(proxyId, { vectors, productId }); return modules.products.findProduct({ productId: proxyId }); } diff --git a/packages/api/src/resolvers/type/product/product-variation-assignment-types.ts b/packages/api/src/resolvers/type/product/product-variation-assignment-types.ts index 1b530924d0..381bd7666c 100644 --- a/packages/api/src/resolvers/type/product/product-variation-assignment-types.ts +++ b/packages/api/src/resolvers/type/product/product-variation-assignment-types.ts @@ -2,7 +2,7 @@ import { ProductVariationAssignmentHelperTypes } from '@unchainedshop/types/prod export const ProductVariationAssignment: ProductVariationAssignmentHelperTypes = { _id: ({ product, assignment }) => { - return `${product._id}:${Object.values(assignment.vector).join('-')}`; + return `${product?._id}:${assignment?.productId}:${Object.values(assignment.vector).join('-')}`; }, vectors: ({ assignment, product }) => { return Object.keys(assignment.vector || {}).map((key) => ({ diff --git a/packages/api/src/schema/mutation.ts b/packages/api/src/schema/mutation.ts index c829693c7b..bde7f95321 100644 --- a/packages/api/src/schema/mutation.ts +++ b/packages/api/src/schema/mutation.ts @@ -527,7 +527,11 @@ export default [ Unlinks a product from a ConfigurableProduct by providing a configuration combination that uniquely identifies a row in the assignment matrix """ - removeProductAssignment(proxyId: ID!, vectors: [ProductAssignmentVectorInput!]!): Product! + removeProductAssignment( + proxyId: ID! + productId: ID! + vectors: [ProductAssignmentVectorInput!]! + ): Product! """ Adds new language along with the user who created it diff --git a/packages/core-products/src/module/configureProductsModule.ts b/packages/core-products/src/module/configureProductsModule.ts index 3f0cb9030c..62e5518df6 100644 --- a/packages/core-products/src/module/configureProductsModule.ts +++ b/packages/core-products/src/module/configureProductsModule.ts @@ -176,7 +176,6 @@ export const configureProductsModule = async ({ ) => { const { proxy } = product; let filtered = [...(proxy.assignments || [])]; - vectors.forEach(({ key, value }) => { filtered = filtered.filter((assignment) => { if (assignment.vector[key] === value) { @@ -264,7 +263,6 @@ export const configureProductsModule = async ({ proxyAssignments: async (product, { includeInactive = false } = {}) => { const assignments = product.proxy?.assignments || []; - const productIds = assignments.map(({ productId }) => productId); const selector: mongodb.Filter = { _id: { $in: productIds }, @@ -277,7 +275,6 @@ export const configureProductsModule = async ({ }) .map(({ _id }) => _id) .toArray(); - return assignments .filter(({ productId }) => { return supportedProductIds.includes(productId); @@ -430,7 +427,7 @@ export const configureProductsModule = async ({ return proxyId; }, - removeAssignment: async (productId, { vectors }) => { + removeAssignment: async (proxyId, { vectors, productId }) => { const vector = {}; vectors.forEach(({ key, value }) => { vector[key] = value; @@ -442,12 +439,13 @@ export const configureProductsModule = async ({ $pull: { 'proxy.assignments': { vector, + productId, }, }, }; - await Products.updateOne(generateDbFilterById(productId), modifier); + await Products.updateOne(generateDbFilterById(proxyId), modifier); - await emit('PRODUCT_REMOVE_ASSIGNMENT', { productId }); + await emit('PRODUCT_REMOVE_ASSIGNMENT', { proxyId, productId }); return vectors.length; }, diff --git a/packages/types/products.ts b/packages/types/products.ts index ff7f0f8ba1..9d393c94df 100644 --- a/packages/types/products.ts +++ b/packages/types/products.ts @@ -332,8 +332,8 @@ export type ProductsModule = { params: { proxyId: string; vectors: Array }, ) => Promise; removeAssignment: ( - productId: string, - params: { vectors: Array }, + proxyId: string, + params: { vectors: Array; productId: string }, ) => Promise; }; From 81f5e6b3cb5a63e3f80d60a62a18b932762812da Mon Sep 17 00:00:00 2001 From: Mikael Araya Date: Tue, 7 Jan 2025 10:06:08 +0300 Subject: [PATCH 2/4] Change productId in removeProductAssignment to optional --- .../resolvers/mutations/products/removeProductAssignment.ts | 2 +- packages/api/src/schema/mutation.ts | 2 +- packages/core-products/src/module/configureProductsModule.ts | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/api/src/resolvers/mutations/products/removeProductAssignment.ts b/packages/api/src/resolvers/mutations/products/removeProductAssignment.ts index 5459eaaa06..24bb533cec 100644 --- a/packages/api/src/resolvers/mutations/products/removeProductAssignment.ts +++ b/packages/api/src/resolvers/mutations/products/removeProductAssignment.ts @@ -6,7 +6,7 @@ import { InvalidIdError, ProductNotFoundError, ProductWrongTypeError } from '../ export default async function removeProductAssignment( root: Root, - params: { proxyId: string; vectors: Array; productId: string }, + params: { proxyId: string; vectors: Array; productId?: string }, { modules, userId }: Context, ) { const { proxyId, vectors, productId } = params; diff --git a/packages/api/src/schema/mutation.ts b/packages/api/src/schema/mutation.ts index bde7f95321..04cdab7ab8 100644 --- a/packages/api/src/schema/mutation.ts +++ b/packages/api/src/schema/mutation.ts @@ -529,7 +529,7 @@ export default [ """ removeProductAssignment( proxyId: ID! - productId: ID! + productId: ID vectors: [ProductAssignmentVectorInput!]! ): Product! diff --git a/packages/core-products/src/module/configureProductsModule.ts b/packages/core-products/src/module/configureProductsModule.ts index 62e5518df6..0af978054a 100644 --- a/packages/core-products/src/module/configureProductsModule.ts +++ b/packages/core-products/src/module/configureProductsModule.ts @@ -439,10 +439,11 @@ export const configureProductsModule = async ({ $pull: { 'proxy.assignments': { vector, - productId, }, }, }; + if (productId) modifier.$pull['proxy.assignments']['productId'] = productId; + await Products.updateOne(generateDbFilterById(proxyId), modifier); await emit('PRODUCT_REMOVE_ASSIGNMENT', { proxyId, productId }); From e6926b925afa0bb0768c6ed098de6d94dec66185 Mon Sep 17 00:00:00 2001 From: Mikael Araya Date: Tue, 7 Jan 2025 11:50:19 +0300 Subject: [PATCH 3/4] Add duplicate vector entry test --- .../resolvers/mutations/products/addProductAssignment.ts | 1 - packages/api/src/schema/mutation.ts | 6 +----- .../core-products/src/module/configureProductsModule.ts | 8 ++++++++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/api/src/resolvers/mutations/products/addProductAssignment.ts b/packages/api/src/resolvers/mutations/products/addProductAssignment.ts index 78d0cc87e4..db2d5eed3b 100644 --- a/packages/api/src/resolvers/mutations/products/addProductAssignment.ts +++ b/packages/api/src/resolvers/mutations/products/addProductAssignment.ts @@ -35,7 +35,6 @@ export default async function addProductAssignment( received: proxyProduct.type, required: ProductTypes.ConfigurableProduct, }); - await modules.products.assignments.addProxyAssignment(productId, { proxyId, vectors, diff --git a/packages/api/src/schema/mutation.ts b/packages/api/src/schema/mutation.ts index 04cdab7ab8..c829693c7b 100644 --- a/packages/api/src/schema/mutation.ts +++ b/packages/api/src/schema/mutation.ts @@ -527,11 +527,7 @@ export default [ Unlinks a product from a ConfigurableProduct by providing a configuration combination that uniquely identifies a row in the assignment matrix """ - removeProductAssignment( - proxyId: ID! - productId: ID - vectors: [ProductAssignmentVectorInput!]! - ): Product! + removeProductAssignment(proxyId: ID!, vectors: [ProductAssignmentVectorInput!]!): Product! """ Adds new language along with the user who created it diff --git a/packages/core-products/src/module/configureProductsModule.ts b/packages/core-products/src/module/configureProductsModule.ts index 0af978054a..b784b6b8a7 100644 --- a/packages/core-products/src/module/configureProductsModule.ts +++ b/packages/core-products/src/module/configureProductsModule.ts @@ -408,6 +408,14 @@ export const configureProductsModule = async ({ vectors.forEach(({ key, value }) => { vector[key] = value; }); + // Check if a similar vector assignment already exists + const existingAssignment = await Products.findOne({ + _id: proxyId, + 'proxy.assignments.vector': vector, + }); + + if (existingAssignment) return; + const modifier = { $set: { updated: new Date(), From 81353d4c65d97eba55f330573bb29f53882ee56a Mon Sep 17 00:00:00 2001 From: Mikael Araya Date: Tue, 7 Jan 2025 12:57:04 +0300 Subject: [PATCH 4/4] Throw error on icomplete proxy configuration vector provided --- packages/api/src/errors.ts | 5 +++ .../products/addProductAssignment.ts | 43 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/api/src/errors.ts b/packages/api/src/errors.ts index 20cf120873..31c0201497 100644 --- a/packages/api/src/errors.ts +++ b/packages/api/src/errors.ts @@ -300,3 +300,8 @@ export const ImpersonatingAdminUserError = createError( export const FileNotFoundError = createError('FileNotFoundError', 'File not found'); export const FileUploadExpiredError = createError('FileUploadExpired', 'File upload has expired'); + +export const ConfigurationVectorInvalid = createError( + 'ConfigurationVectorInvalid', + 'Provider Configuration vector is invalid/incomplete please provide all available configuration vectors', +); diff --git a/packages/api/src/resolvers/mutations/products/addProductAssignment.ts b/packages/api/src/resolvers/mutations/products/addProductAssignment.ts index db2d5eed3b..0b87c0e4ae 100644 --- a/packages/api/src/resolvers/mutations/products/addProductAssignment.ts +++ b/packages/api/src/resolvers/mutations/products/addProductAssignment.ts @@ -3,7 +3,35 @@ import { ProductTypes } from '@unchainedshop/core-products'; import { Context, Root } from '@unchainedshop/types/api.js'; import { ProductConfiguration } from '@unchainedshop/types/products.js'; -import { ProductNotFoundError, InvalidIdError, ProductWrongTypeError } from '../../../errors.js'; +import { + ProductNotFoundError, + InvalidIdError, + ProductWrongTypeError, + ConfigurationVectorInvalid, +} from '../../../errors.js'; +const extractVariationMatrix = (variations = []) => { + const cartesianProduct = (arrays) => { + return arrays.reduce( + (acc, array) => acc.flatMap((item) => array.map((value) => [...item, value])), + [[]], + ); + }; + const keys = variations.map((item) => item.key); + const options = variations.map((item) => item.options); + const combinations = cartesianProduct(options); + return combinations.map((combination) => + combination.reduce((acc, value, index) => { + acc[keys[index]] = value; + return acc; + }, {}), + ); +}; + +const combinationExists = (matrix, combination) => { + return matrix.some((variation) => { + return Object.entries(combination).every(([key, value]) => variation[key] === value); + }); +}; export default async function addProductAssignment( root: Root, @@ -35,6 +63,19 @@ export default async function addProductAssignment( received: proxyProduct.type, required: ProductTypes.ConfigurableProduct, }); + const variations = await modules.products.variations.findProductVariations({ + productId: proxyId, + }); + const variationMatrix = extractVariationMatrix(variations); + const normalizedVectors = vectors?.reduce((prev, { key, value }) => { + return { + ...prev, + [key]: value, + }; + }, {}); + if (!combinationExists(variationMatrix, normalizedVectors)) + throw new ConfigurationVectorInvalid({ proxyId, vectors }); + await modules.products.assignments.addProxyAssignment(productId, { proxyId, vectors,