From 651eea0bdda1ed0f46f4c73d7edf52c8c1da5b54 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Fri, 15 Nov 2024 14:01:09 +0100 Subject: [PATCH 1/9] Created a new field for products: `custom_attribute_option(attribute_code: "attribute_code")` to retrieve attribute option value labels. This field is only available in Magento 2.4.7 and up. --- .changeset/chatty-adults-run.md | 5 + packages/magento-graphql/mesh/resolvers.ts | 133 ++++++++++++++++++ .../plugins/meshConfigAttrValue.ts | 23 +++ .../schema/CustomAttributeOption.graphqls | 37 +++++ 4 files changed, 198 insertions(+) create mode 100644 .changeset/chatty-adults-run.md create mode 100644 packages/magento-graphql/mesh/resolvers.ts create mode 100644 packages/magento-graphql/plugins/meshConfigAttrValue.ts create mode 100644 packages/magento-graphql/schema/CustomAttributeOption.graphqls diff --git a/.changeset/chatty-adults-run.md b/.changeset/chatty-adults-run.md new file mode 100644 index 0000000000..a8e66c1352 --- /dev/null +++ b/.changeset/chatty-adults-run.md @@ -0,0 +1,5 @@ +--- +'@graphcommerce/magento-graphql': minor +--- + +Created a new field for products: `custom_attribute_option(attribute_code: "attribute_code")` to retrieve attribute option value labels. This field is only available in Magento 2.4.7 and up. diff --git a/packages/magento-graphql/mesh/resolvers.ts b/packages/magento-graphql/mesh/resolvers.ts new file mode 100644 index 0000000000..a10e458da5 --- /dev/null +++ b/packages/magento-graphql/mesh/resolvers.ts @@ -0,0 +1,133 @@ +import fragments from '@graphcommerce/graphql/generated/fragments.json' +import type { + AttributeInput, + CustomAttributeOptionOutput, + MeshContext, + ProductInterfaceResolvers, + Resolvers, + ResolversTypes, +} from '@graphcommerce/graphql-mesh' +import { filterNonNullableKeys } from '@graphcommerce/next-ui' +import { print, Kind, SelectionSetNode, StringValueNode } from 'graphql' + +type CustomAttributeInput = { attribute_code: string; entity_type: 'catalog_product' } + +async function customAttributeMetadataV2(input: CustomAttributeInput, context: MeshContext) { + const cacheKey = `customAttributeMetadata-${input.entity_type}-${input.attribute_code}` + const cached = await context.cache.get(cacheKey) + if (cached) return cached + + if (input.entity_type !== 'catalog_product') + throw Error('Only catalog_product is supported at this moment') + + const response = context.m2.Query.customAttributeMetadataV2({ + context, + key: input, + argsFromKeys: (attributes) => ({ attributes }), + selectionSet: /* GraphQL */ ` + { + items { + __typename + code + label + default_value + entity_type + frontend_class + frontend_input + is_required + is_unique + label + ... on CatalogAttributeMetadata { + apply_to + is_comparable + is_filterable + is_filterable_in_search + is_html_allowed_on_front + is_searchable + is_used_for_price_rules + is_used_for_promo_rules + is_visible_in_advanced_search + is_visible_on_front + is_wysiwyg_enabled + used_in_product_listing + } + options { + label + is_default + value + } + } + } + `, + valuesFromResults: (values, attributes) => { + const all = filterNonNullableKeys(values.items) + return attributes.map((attribute) => all.find((v) => v.code === attribute.attribute_code)) + }, + }) + + // Cache for 1 hour + await context.cache.set(cacheKey, response, { ttl: 60 * 60 }) + return response +} + +type ProductResolver = Pick, 'custom_attribute_option'> + +const productResolver: ProductResolver = { + custom_attribute_option: { + selectionSet: (fieldNode) => { + return { + kind: Kind.SELECTION_SET, + selections: (fieldNode.arguments ?? []) + .map((arg) => arg.value) + .filter((value) => value.kind === Kind.STRING) + .map((value) => ({ kind: Kind.FIELD, name: { kind: Kind.NAME, value: value.value } })), + } + }, + resolve: async (root, args, context) => { + if (import.meta.graphCommerce.magentoVersion <= 246) { + throw Error('This field is only available in Magento 2.4.7 and up') + } + + type Result = ResolversTypes['CustomAttributeOptionOutput'] + const result: Result = { raw: `${root[args.code]}`, options: [], attribute: null, errors: [] } + + const values = result.raw.includes(',') + ? [result.raw, ...result.raw.split(',')] + : [result.raw] + const attribute = await customAttributeMetadataV2( + { attribute_code: args.code, entity_type: 'catalog_product' }, + context, + ) + + result.attribute = attribute + + if (!attribute) { + const message = `Attribute '${args.code}' found, but option ${values.join(', ')} not found` + result.errors.push({ message, type: 'ATTRIBUTE_NOT_FOUND' }) + return result + } + + values.forEach((v) => { + const found = attribute.options?.find((o) => o?.value === v) + if (found) { + result.options?.push(found) + } else { + const message = `Option '${v}' not found for attribute '${args.code}'` + result.errors.push({ message, type: 'ATTRIBUTE_NOT_FOUND' }) + } + }) + return result + }, + }, +} + +const resolvers: Resolvers = {} + +type ProductTypes = NonNullable>> +const productInterfaceTypes = fragments.possibleTypes.ProductInterface as ProductTypes[] + +productInterfaceTypes.forEach((productType) => { + if (!resolvers[productType]) resolvers[productType] = productResolver +}) + +export default resolvers diff --git a/packages/magento-graphql/plugins/meshConfigAttrValue.ts b/packages/magento-graphql/plugins/meshConfigAttrValue.ts new file mode 100644 index 0000000000..c3f5a77ad6 --- /dev/null +++ b/packages/magento-graphql/plugins/meshConfigAttrValue.ts @@ -0,0 +1,23 @@ +import type { meshConfig as meshConfigBase } from '@graphcommerce/graphql-mesh/meshConfig' +import type { FunctionPlugin, PluginConfig } from '@graphcommerce/next-config' + +export const config: PluginConfig = { + module: '@graphcommerce/graphql-mesh/meshConfig', + type: 'function', +} + +export const meshConfig: FunctionPlugin = ( + prev, + baseConfig, + graphCommerceConfig, +) => + prev( + { + ...baseConfig, + additionalResolvers: [ + ...(baseConfig.additionalResolvers ?? []), + '@graphcommerce/magento-graphql/mesh/resolvers.ts', + ], + }, + graphCommerceConfig, + ) diff --git a/packages/magento-graphql/schema/CustomAttributeOption.graphqls b/packages/magento-graphql/schema/CustomAttributeOption.graphqls new file mode 100644 index 0000000000..2dfb546b13 --- /dev/null +++ b/packages/magento-graphql/schema/CustomAttributeOption.graphqls @@ -0,0 +1,37 @@ +type CustomAttributeOptionOutput { + raw: String! + """ + Errors of retrieving certain attributes metadata. + """ + errors: [AttributeMetadataError]! + """ + Attribute options. + """ + options: [CustomAttributeOptionInterface] + """ + Requested attribute metadata. + """ + attribute: CustomAttributeMetadataInterface +} + +extend interface ProductInterface { + custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput +} +extend type SimpleProduct { + custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput +} +extend type ConfigurableProduct { + custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput +} +extend type BundleProduct { + custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput +} +extend type DownloadableProduct { + custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput +} +extend type VirtualProduct { + custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput +} +extend type GroupedProduct { + custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput +} From fcb55485d752aa8f0f5b475724f2fe6418aebedc Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Fri, 15 Nov 2024 14:57:23 +0100 Subject: [PATCH 2/9] Fix build --- packages/magento-graphql-rest/package.json | 4 +- packages/magento-graphql/mesh/resolvers.ts | 52 ++++++++++++---------- packages/magento-graphql/package.json | 3 +- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/packages/magento-graphql-rest/package.json b/packages/magento-graphql-rest/package.json index d0ed0fbb08..0056b38219 100644 --- a/packages/magento-graphql-rest/package.json +++ b/packages/magento-graphql-rest/package.json @@ -20,10 +20,10 @@ "@graphcommerce/magento-product": "^9.0.0-canary.98", "@graphcommerce/magento-search": "^9.0.0-canary.98", "@graphcommerce/next-config": "^9.0.0-canary.98", - "@graphcommerce/next-ui": "^9.0.0-canary.98" + "@graphcommerce/next-ui": "^9.0.0-canary.98", + "graphql": "^16.0.0" }, "devDependencies": { - "graphql": "^16.0.0", "tsx": "^4.16.2" } } diff --git a/packages/magento-graphql/mesh/resolvers.ts b/packages/magento-graphql/mesh/resolvers.ts index a10e458da5..dd0a7bb9d3 100644 --- a/packages/magento-graphql/mesh/resolvers.ts +++ b/packages/magento-graphql/mesh/resolvers.ts @@ -1,18 +1,19 @@ import fragments from '@graphcommerce/graphql/generated/fragments.json' import type { - AttributeInput, - CustomAttributeOptionOutput, + CustomAttributeMetadataInterface, MeshContext, ProductInterfaceResolvers, Resolvers, ResolversTypes, } from '@graphcommerce/graphql-mesh' -import { filterNonNullableKeys } from '@graphcommerce/next-ui' -import { print, Kind, SelectionSetNode, StringValueNode } from 'graphql' +import { Kind } from 'graphql' type CustomAttributeInput = { attribute_code: string; entity_type: 'catalog_product' } -async function customAttributeMetadataV2(input: CustomAttributeInput, context: MeshContext) { +async function customAttributeMetadataV2( + input: CustomAttributeInput, + context: MeshContext, +): Promise { const cacheKey = `customAttributeMetadata-${input.entity_type}-${input.attribute_code}` const cached = await context.cache.get(cacheKey) if (cached) return cached @@ -59,10 +60,10 @@ async function customAttributeMetadataV2(input: CustomAttributeInput, context: M } } `, - valuesFromResults: (values, attributes) => { - const all = filterNonNullableKeys(values.items) - return attributes.map((attribute) => all.find((v) => v.code === attribute.attribute_code)) - }, + valuesFromResults: (values, attributes) => + attributes.map((attribute) => + values.items?.find((v) => v?.code === attribute.attribute_code), + ), }) // Cache for 1 hour @@ -74,45 +75,48 @@ type ProductResolver = Pick, 'custom_attr const productResolver: ProductResolver = { custom_attribute_option: { - selectionSet: (fieldNode) => { - return { - kind: Kind.SELECTION_SET, - selections: (fieldNode.arguments ?? []) - .map((arg) => arg.value) - .filter((value) => value.kind === Kind.STRING) - .map((value) => ({ kind: Kind.FIELD, name: { kind: Kind.NAME, value: value.value } })), - } - }, - resolve: async (root, args, context) => { + selectionSet: (fieldNode) => ({ + kind: Kind.SELECTION_SET, + selections: (fieldNode.arguments ?? []) + .map((arg) => arg.value) + .filter((value) => value.kind === Kind.STRING) + .map((value) => ({ kind: Kind.FIELD, name: { kind: Kind.NAME, value: value.value } })), + }), + resolve: async (root, { attribute_code }, context) => { if (import.meta.graphCommerce.magentoVersion <= 246) { throw Error('This field is only available in Magento 2.4.7 and up') } type Result = ResolversTypes['CustomAttributeOptionOutput'] - const result: Result = { raw: `${root[args.code]}`, options: [], attribute: null, errors: [] } + const result: Result = { + raw: `${root[attribute_code]}`, + options: [], + attribute: null, + errors: [], + } const values = result.raw.includes(',') ? [result.raw, ...result.raw.split(',')] : [result.raw] const attribute = await customAttributeMetadataV2( - { attribute_code: args.code, entity_type: 'catalog_product' }, + { attribute_code, entity_type: 'catalog_product' }, context, ) result.attribute = attribute if (!attribute) { - const message = `Attribute '${args.code}' found, but option ${values.join(', ')} not found` + const message = `Attribute '${attribute_code}' found, but option ${values.join(', ')} not found` result.errors.push({ message, type: 'ATTRIBUTE_NOT_FOUND' }) return result } values.forEach((v) => { - const found = attribute.options?.find((o) => o?.value === v) + const found = attribute.options?.find((o) => o?.value === v || o?.label === v) if (found) { result.options?.push(found) } else { - const message = `Option '${v}' not found for attribute '${args.code}'` + const message = `Option '${v}' not found for attribute '${attribute_code}'` result.errors.push({ message, type: 'ATTRIBUTE_NOT_FOUND' }) } }) diff --git a/packages/magento-graphql/package.json b/packages/magento-graphql/package.json index 3287148cfb..363ff60bac 100644 --- a/packages/magento-graphql/package.json +++ b/packages/magento-graphql/package.json @@ -19,6 +19,7 @@ "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.98", "next": "*", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "graphql": "^16.0.0" } } From 8e5340c0e476290687f04314fac099f488b4da1a Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 18 Nov 2024 10:04:57 +0100 Subject: [PATCH 3/9] Solve build issues with Magento 2.4.6 --- packages/magento-graphql/mesh/resolvers.ts | 10 ++++++---- packages/magento-graphql/package.json | 4 ++-- .../Query-customAttributeMetadataV2.graphqls | 7 +++++++ yarn.lock | 3 ++- 4 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 packages/magento-graphql/schema-247/Query-customAttributeMetadataV2.graphqls diff --git a/packages/magento-graphql/mesh/resolvers.ts b/packages/magento-graphql/mesh/resolvers.ts index dd0a7bb9d3..52f1a4d940 100644 --- a/packages/magento-graphql/mesh/resolvers.ts +++ b/packages/magento-graphql/mesh/resolvers.ts @@ -21,6 +21,12 @@ async function customAttributeMetadataV2( if (input.entity_type !== 'catalog_product') throw Error('Only catalog_product is supported at this moment') + if ( + !('customAttributeMetadataV2' in context.m2.Query) || + typeof context.m2.Query.customAttributeMetadataV2 !== 'function' + ) + throw Error('This field is only available in Magento 2.4.7 and up') + const response = context.m2.Query.customAttributeMetadataV2({ context, key: input, @@ -83,10 +89,6 @@ const productResolver: ProductResolver = { .map((value) => ({ kind: Kind.FIELD, name: { kind: Kind.NAME, value: value.value } })), }), resolve: async (root, { attribute_code }, context) => { - if (import.meta.graphCommerce.magentoVersion <= 246) { - throw Error('This field is only available in Magento 2.4.7 and up') - } - type Result = ResolversTypes['CustomAttributeOptionOutput'] const result: Result = { raw: `${root[attribute_code]}`, diff --git a/packages/magento-graphql/package.json b/packages/magento-graphql/package.json index 363ff60bac..3d1ca2d1ca 100644 --- a/packages/magento-graphql/package.json +++ b/packages/magento-graphql/package.json @@ -17,9 +17,9 @@ "@graphcommerce/graphql": "^9.0.0-canary.98", "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.98", "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.98", + "graphql": "^16.0.0", "next": "*", "react": "^18.2.0", - "react-dom": "^18.2.0", - "graphql": "^16.0.0" + "react-dom": "^18.2.0" } } diff --git a/packages/magento-graphql/schema-247/Query-customAttributeMetadataV2.graphqls b/packages/magento-graphql/schema-247/Query-customAttributeMetadataV2.graphqls new file mode 100644 index 0000000000..aae0a6eae9 --- /dev/null +++ b/packages/magento-graphql/schema-247/Query-customAttributeMetadataV2.graphqls @@ -0,0 +1,7 @@ +type Query { + """ + Retrieve EAV attributes metadata. + """ + customAttributeMetadataV2(attributes: [AttributeInput!]): AttributesMetadataOutput! + @deprecated(reason: "Magento >= 2.4.7") +} diff --git a/yarn.lock b/yarn.lock index 41587e0888..3a21ea4773 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4087,7 +4087,6 @@ __metadata: version: 0.0.0-use.local resolution: "@graphcommerce/magento-graphql-rest@workspace:packages/magento-graphql-rest" dependencies: - graphql: "npm:^16.0.0" tsx: "npm:^4.16.2" peerDependencies: "@graphcommerce/graphql-mesh": ^9.0.0-canary.98 @@ -4095,6 +4094,7 @@ __metadata: "@graphcommerce/magento-search": ^9.0.0-canary.98 "@graphcommerce/next-config": ^9.0.0-canary.98 "@graphcommerce/next-ui": ^9.0.0-canary.98 + graphql: ^16.0.0 languageName: unknown linkType: soft @@ -4106,6 +4106,7 @@ __metadata: "@graphcommerce/graphql": ^9.0.0-canary.98 "@graphcommerce/prettier-config-pwa": ^9.0.0-canary.98 "@graphcommerce/typescript-config-pwa": ^9.0.0-canary.98 + graphql: ^16.0.0 next: "*" react: ^18.2.0 react-dom: ^18.2.0 From 67dad937232fc8ce277010214f5f3eb3f004b947 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 18 Nov 2024 10:29:20 +0100 Subject: [PATCH 4/9] Solve test error --- .../next-config/__tests__/interceptors/findPlugins.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packagesDev/next-config/__tests__/interceptors/findPlugins.ts b/packagesDev/next-config/__tests__/interceptors/findPlugins.ts index 3effd87ce3..94d9f45ef6 100644 --- a/packagesDev/next-config/__tests__/interceptors/findPlugins.ts +++ b/packagesDev/next-config/__tests__/interceptors/findPlugins.ts @@ -503,6 +503,14 @@ it('finds plugins', () => { "targetModule": "@graphcommerce/graphql", "type": "function", }, + { + "enabled": true, + "sourceExport": "meshConfig", + "sourceModule": "@graphcommerce/magento-graphql/plugins/meshConfigAttrValue", + "targetExport": "meshConfig", + "targetModule": "@graphcommerce/graphql-mesh/meshConfig", + "type": "function", + }, { "enabled": true, "sourceExport": "graphqlConfig", From 14bb748dc9210389126615de1c9ed30b1f606318 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 18 Nov 2024 11:17:39 +0100 Subject: [PATCH 5/9] Added comments to schema --- .../schema/CustomAttributeOption.graphqls | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/magento-graphql/schema/CustomAttributeOption.graphqls b/packages/magento-graphql/schema/CustomAttributeOption.graphqls index 2dfb546b13..dc6d6e603b 100644 --- a/packages/magento-graphql/schema/CustomAttributeOption.graphqls +++ b/packages/magento-graphql/schema/CustomAttributeOption.graphqls @@ -15,23 +15,44 @@ type CustomAttributeOptionOutput { } extend interface ProductInterface { + """ + Returns the selected attribute option values for the product as well as the attribute metadata. + """ custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput } extend type SimpleProduct { + """ + Returns the selected attribute option values for the product as well as the attribute metadata. + """ custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput } extend type ConfigurableProduct { + """ + Returns the selected attribute option values for the product as well as the attribute metadata. + """ custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput } extend type BundleProduct { + """ + Returns the selected attribute option values for the product as well as the attribute metadata. + """ custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput } extend type DownloadableProduct { + """ + Returns the selected attribute option values for the product as well as the attribute metadata. + """ custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput } extend type VirtualProduct { + """ + Returns the selected attribute option values for the product as well as the attribute metadata. + """ custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput } extend type GroupedProduct { + """ + Returns the selected attribute option values for the product as well as the attribute metadata. + """ custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput } From 5591151a8fc286629303a774a627b34a5660c54c Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 18 Nov 2024 12:11:33 +0100 Subject: [PATCH 6/9] Refactored to use AttributeValueInterface directly and add the CustomAttributeMetadataInterface to the AttributeValueInterface --- .changeset/chatty-adults-run.md | 2 +- packages/magento-graphql/mesh/resolvers.ts | 70 +++++++------------ .../schema/CustomAttributeOption.graphqls | 58 --------------- ...ProductInterface-custom_attribute.graphqls | 59 ++++++++++++++++ 4 files changed, 86 insertions(+), 103 deletions(-) delete mode 100644 packages/magento-graphql/schema/CustomAttributeOption.graphqls create mode 100644 packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls diff --git a/.changeset/chatty-adults-run.md b/.changeset/chatty-adults-run.md index a8e66c1352..be35673e39 100644 --- a/.changeset/chatty-adults-run.md +++ b/.changeset/chatty-adults-run.md @@ -2,4 +2,4 @@ '@graphcommerce/magento-graphql': minor --- -Created a new field for products: `custom_attribute_option(attribute_code: "attribute_code")` to retrieve attribute option value labels. This field is only available in Magento 2.4.7 and up. +Created a new field for products: `custom_attribute(attribute_code: "attribute_code")` to retrieve attribute option value labels. This field is only available in Magento 2.4.7 and up. diff --git a/packages/magento-graphql/mesh/resolvers.ts b/packages/magento-graphql/mesh/resolvers.ts index 52f1a4d940..eabdedf178 100644 --- a/packages/magento-graphql/mesh/resolvers.ts +++ b/packages/magento-graphql/mesh/resolvers.ts @@ -4,7 +4,6 @@ import type { MeshContext, ProductInterfaceResolvers, Resolvers, - ResolversTypes, } from '@graphcommerce/graphql-mesh' import { Kind } from 'graphql' @@ -27,7 +26,7 @@ async function customAttributeMetadataV2( ) throw Error('This field is only available in Magento 2.4.7 and up') - const response = context.m2.Query.customAttributeMetadataV2({ + const attribute = context.m2.Query.customAttributeMetadataV2({ context, key: input, argsFromKeys: (attributes) => ({ attributes }), @@ -66,21 +65,19 @@ async function customAttributeMetadataV2( } } `, - valuesFromResults: (values, attributes) => - attributes.map((attribute) => - values.items?.find((v) => v?.code === attribute.attribute_code), - ), + valuesFromResults: (res, attributes) => + attributes.map((attr) => res.items?.find((v) => v?.code === attr.attribute_code)), }) // Cache for 1 hour - await context.cache.set(cacheKey, response, { ttl: 60 * 60 }) - return response + await context.cache.set(cacheKey, attribute, { ttl: 60 * 60 }) + return attribute } -type ProductResolver = Pick, 'custom_attribute_option'> +type ProductResolver = Pick, 'custom_attributeV2'> const productResolver: ProductResolver = { - custom_attribute_option: { + custom_attributeV2: { selectionSet: (fieldNode) => ({ kind: Kind.SELECTION_SET, selections: (fieldNode.arguments ?? []) @@ -88,41 +85,26 @@ const productResolver: ProductResolver = { .filter((value) => value.kind === Kind.STRING) .map((value) => ({ kind: Kind.FIELD, name: { kind: Kind.NAME, value: value.value } })), }), - resolve: async (root, { attribute_code }, context) => { - type Result = ResolversTypes['CustomAttributeOptionOutput'] - const result: Result = { - raw: `${root[attribute_code]}`, - options: [], - attribute: null, - errors: [], + resolve: async (root, { attribute_code: code }, context) => { + const value = String(root[code] ?? '') + const input: CustomAttributeInput = { attribute_code: code, entity_type: 'catalog_product' } + const attribute = await customAttributeMetadataV2(input, context) + + if (!attribute || !value) return null + + if ( + attribute?.frontend_input && + ['SELECT', 'MULTISELECT'].includes(attribute.frontend_input) + ) { + const values = attribute.frontend_input === 'SELECT' ? [value] : value.split(',') + const selected_options = values.map((v) => { + const found = attribute.options?.find((o) => o?.value === v || o?.label === v) + if (!found) return null + return { ...found, __typename: 'AttributeSelectedOption' } + }) + return { __typename: 'AttributeSelectedOptions', code, selected_options, attribute } } - - const values = result.raw.includes(',') - ? [result.raw, ...result.raw.split(',')] - : [result.raw] - const attribute = await customAttributeMetadataV2( - { attribute_code, entity_type: 'catalog_product' }, - context, - ) - - result.attribute = attribute - - if (!attribute) { - const message = `Attribute '${attribute_code}' found, but option ${values.join(', ')} not found` - result.errors.push({ message, type: 'ATTRIBUTE_NOT_FOUND' }) - return result - } - - values.forEach((v) => { - const found = attribute.options?.find((o) => o?.value === v || o?.label === v) - if (found) { - result.options?.push(found) - } else { - const message = `Option '${v}' not found for attribute '${attribute_code}'` - result.errors.push({ message, type: 'ATTRIBUTE_NOT_FOUND' }) - } - }) - return result + return { __typename: 'AttributeValue', code, value, attribute } }, }, } diff --git a/packages/magento-graphql/schema/CustomAttributeOption.graphqls b/packages/magento-graphql/schema/CustomAttributeOption.graphqls deleted file mode 100644 index dc6d6e603b..0000000000 --- a/packages/magento-graphql/schema/CustomAttributeOption.graphqls +++ /dev/null @@ -1,58 +0,0 @@ -type CustomAttributeOptionOutput { - raw: String! - """ - Errors of retrieving certain attributes metadata. - """ - errors: [AttributeMetadataError]! - """ - Attribute options. - """ - options: [CustomAttributeOptionInterface] - """ - Requested attribute metadata. - """ - attribute: CustomAttributeMetadataInterface -} - -extend interface ProductInterface { - """ - Returns the selected attribute option values for the product as well as the attribute metadata. - """ - custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput -} -extend type SimpleProduct { - """ - Returns the selected attribute option values for the product as well as the attribute metadata. - """ - custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput -} -extend type ConfigurableProduct { - """ - Returns the selected attribute option values for the product as well as the attribute metadata. - """ - custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput -} -extend type BundleProduct { - """ - Returns the selected attribute option values for the product as well as the attribute metadata. - """ - custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput -} -extend type DownloadableProduct { - """ - Returns the selected attribute option values for the product as well as the attribute metadata. - """ - custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput -} -extend type VirtualProduct { - """ - Returns the selected attribute option values for the product as well as the attribute metadata. - """ - custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput -} -extend type GroupedProduct { - """ - Returns the selected attribute option values for the product as well as the attribute metadata. - """ - custom_attribute_option(attribute_code: String!): CustomAttributeOptionOutput -} diff --git a/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls b/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls new file mode 100644 index 0000000000..618f3bb8b1 --- /dev/null +++ b/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls @@ -0,0 +1,59 @@ +extend interface AttributeValueInterface { + attribute: CustomAttributeMetadataInterface +} +extend type AttributeValue { + attribute: CustomAttributeMetadataInterface +} +extend type AttributeSelectedOptions { + attribute: CustomAttributeMetadataInterface +} + +extend interface ProductInterface { + """ + This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. + """ + custom_attributeV2(attribute_code: String!): AttributeValueInterface +} +extend type SimpleProduct { + """ + This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. + """ + custom_attributeV2(attribute_code: String!): AttributeValueInterface +} +extend type ConfigurableProduct { + """ + This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. + """ + custom_attributeV2(attribute_code: String!): AttributeValueInterface +} +extend type BundleProduct { + """ + This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. + """ + custom_attributeV2(attribute_code: String!): AttributeValueInterface +} +extend type DownloadableProduct { + """ + This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. + """ + custom_attributeV2(attribute_code: String!): AttributeValueInterface +} +extend type VirtualProduct { + """ + This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. + """ + custom_attributeV2(attribute_code: String!): AttributeValueInterface +} +extend type GroupedProduct { + """ + This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. + """ + custom_attributeV2(attribute_code: String!): AttributeValueInterface +} From 3a1f823c9da6d65b10b1fcd457ba642ce92880f9 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 18 Nov 2024 12:16:42 +0100 Subject: [PATCH 7/9] Docs change --- .../ProductInterface-custom_attribute.graphqls | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls b/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls index 618f3bb8b1..d0786d36a9 100644 --- a/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls +++ b/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls @@ -10,49 +10,49 @@ extend type AttributeSelectedOptions { extend interface ProductInterface { """ - This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + This is the singular version of the custom_attributesV2 and allows selecting a single attribute option value. Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. """ custom_attributeV2(attribute_code: String!): AttributeValueInterface } extend type SimpleProduct { """ - This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + This is the singular version of the custom_attributesV2 and allows selecting a single attribute option value. Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. """ custom_attributeV2(attribute_code: String!): AttributeValueInterface } extend type ConfigurableProduct { """ - This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + This is the singular version of the custom_attributesV2 and allows selecting a single attribute option value. Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. """ custom_attributeV2(attribute_code: String!): AttributeValueInterface } extend type BundleProduct { """ - This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + This is the singular version of the custom_attributesV2 and allows selecting a single attribute option value. Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. """ custom_attributeV2(attribute_code: String!): AttributeValueInterface } extend type DownloadableProduct { """ - This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + This is the singular version of the custom_attributesV2 and allows selecting a single attribute option value. Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. """ custom_attributeV2(attribute_code: String!): AttributeValueInterface } extend type VirtualProduct { """ - This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + This is the singular version of the custom_attributesV2 and allows selecting a single attribute option value. Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. """ custom_attributeV2(attribute_code: String!): AttributeValueInterface } extend type GroupedProduct { """ - This is the singular version of the custom_attributeV2sV2 and allows selecting a single attribute option value. + This is the singular version of the custom_attributesV2 and allows selecting a single attribute option value. Available only after Magento 2.4.7 as it relies on the customAttributesV2 query. """ custom_attributeV2(attribute_code: String!): AttributeValueInterface From b078adcf29edb093ab80e976d10551db05eb3e98 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 18 Nov 2024 12:25:49 +0100 Subject: [PATCH 8/9] Added attributeValueResolver to be able to retrieve labels for any attribute value --- packages/magento-graphql/mesh/resolvers.ts | 35 ++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/magento-graphql/mesh/resolvers.ts b/packages/magento-graphql/mesh/resolvers.ts index eabdedf178..e09c6e3010 100644 --- a/packages/magento-graphql/mesh/resolvers.ts +++ b/packages/magento-graphql/mesh/resolvers.ts @@ -1,5 +1,6 @@ import fragments from '@graphcommerce/graphql/generated/fragments.json' import type { + AttributeValueInterfaceResolvers, CustomAttributeMetadataInterface, MeshContext, ProductInterfaceResolvers, @@ -12,7 +13,7 @@ type CustomAttributeInput = { attribute_code: string; entity_type: 'catalog_prod async function customAttributeMetadataV2( input: CustomAttributeInput, context: MeshContext, -): Promise { +): Promise { const cacheKey = `customAttributeMetadata-${input.entity_type}-${input.attribute_code}` const cached = await context.cache.get(cacheKey) if (cached) return cached @@ -26,7 +27,7 @@ async function customAttributeMetadataV2( ) throw Error('This field is only available in Magento 2.4.7 and up') - const attribute = context.m2.Query.customAttributeMetadataV2({ + const attribute = await context.m2.Query.customAttributeMetadataV2({ context, key: input, argsFromKeys: (attributes) => ({ attributes }), @@ -71,12 +72,12 @@ async function customAttributeMetadataV2( // Cache for 1 hour await context.cache.set(cacheKey, attribute, { ttl: 60 * 60 }) - return attribute + return attribute ?? null } -type ProductResolver = Pick, 'custom_attributeV2'> +type CustomAttributeV2Resolver = Pick, 'custom_attributeV2'> -const productResolver: ProductResolver = { +const customAttributeV2Resolver: CustomAttributeV2Resolver = { custom_attributeV2: { selectionSet: (fieldNode) => ({ kind: Kind.SELECTION_SET, @@ -115,7 +116,29 @@ type ProductTypes = NonNullable { - if (!resolvers[productType]) resolvers[productType] = productResolver + if (!resolvers[productType]) resolvers[productType] = customAttributeV2Resolver +}) + +const attributeValueResolver: AttributeValueResolver = { + attribute: { + selectionSet: `{ code }`, + resolve: async (root, _, context) => + root.attribute ?? + (await customAttributeMetadataV2( + { attribute_code: root.code, entity_type: 'catalog_product' }, + context, + )), + }, +} + +type AttributeValueResolver = Pick, 'attribute'> +type AttributeValueTypes = NonNullable< + Awaited> +> +const attributeValueTypes = fragments.possibleTypes.AttributeValueInterface as AttributeValueTypes[] + +attributeValueTypes.forEach((attributeValueType) => { + if (!resolvers[attributeValueType]) resolvers[attributeValueType] = attributeValueResolver }) export default resolvers From 53af25671d3aca7f3daa2dd45ccd2237697e9254 Mon Sep 17 00:00:00 2001 From: Paul Hachmang Date: Mon, 18 Nov 2024 12:47:33 +0100 Subject: [PATCH 9/9] Added an `attribute`-field to `AttributeValueInterface` to be able to retrieve attribute metadata from the value of an attribute. Refactored code --- .changeset/soft-dogs-hide.md | 5 + .../mesh/attributeValueResolver.ts | 31 ++++ .../mesh/customAttributeMetadataV2.ts | 68 +++++++++ .../mesh/customAttributeV2Resolver.ts | 47 ++++++ packages/magento-graphql/mesh/resolvers.ts | 144 ------------------ .../plugins/meshConfigAttrValue.ts | 3 +- ...AttributeValueInterface-attribute.graphqls | 9 ++ ...ProductInterface-custom_attribute.graphqls | 10 -- 8 files changed, 162 insertions(+), 155 deletions(-) create mode 100644 .changeset/soft-dogs-hide.md create mode 100644 packages/magento-graphql/mesh/attributeValueResolver.ts create mode 100644 packages/magento-graphql/mesh/customAttributeMetadataV2.ts create mode 100644 packages/magento-graphql/mesh/customAttributeV2Resolver.ts delete mode 100644 packages/magento-graphql/mesh/resolvers.ts create mode 100644 packages/magento-graphql/schema/AttributeValueInterface-attribute.graphqls diff --git a/.changeset/soft-dogs-hide.md b/.changeset/soft-dogs-hide.md new file mode 100644 index 0000000000..7607858a24 --- /dev/null +++ b/.changeset/soft-dogs-hide.md @@ -0,0 +1,5 @@ +--- +'@graphcommerce/magento-graphql': minor +--- + +Added an `attribute`-field to `AttributeValueInterface` to be able to retrieve attribute metadata from the value of an attribute. diff --git a/packages/magento-graphql/mesh/attributeValueResolver.ts b/packages/magento-graphql/mesh/attributeValueResolver.ts new file mode 100644 index 0000000000..eaebb9b50f --- /dev/null +++ b/packages/magento-graphql/mesh/attributeValueResolver.ts @@ -0,0 +1,31 @@ +import fragments from '@graphcommerce/graphql/generated/fragments.json' +import type { + AttributeValueInterfaceResolvers, + MeshContext, + Resolvers, +} from '@graphcommerce/graphql-mesh' +import { customAttributeMetadataV2 } from './customAttributeMetadataV2' + +type AttributeValueResolver = Pick, 'attribute'> + +const attributeValueResolver: AttributeValueResolver = { + attribute: { + selectionSet: `{ code }`, + resolve: async (root, _, context) => + root.attribute ?? + (await customAttributeMetadataV2( + { attribute_code: root.code, entity_type: 'catalog_product' }, + context, + )), + }, +} + +type AttributeValueTypes = NonNullable< + Awaited> +> +const attributeValueTypes = fragments.possibleTypes.AttributeValueInterface as AttributeValueTypes[] +const resolvers: Resolvers = {} +attributeValueTypes.forEach((attributeValueType) => { + if (!resolvers[attributeValueType]) resolvers[attributeValueType] = attributeValueResolver +}) +export default resolvers diff --git a/packages/magento-graphql/mesh/customAttributeMetadataV2.ts b/packages/magento-graphql/mesh/customAttributeMetadataV2.ts new file mode 100644 index 0000000000..f738faf907 --- /dev/null +++ b/packages/magento-graphql/mesh/customAttributeMetadataV2.ts @@ -0,0 +1,68 @@ +import type { MeshContext, CustomAttributeMetadataInterface } from '@graphcommerce/graphql-mesh' + +export type CustomAttributeInput = { attribute_code: string; entity_type: 'catalog_product' } + +export async function customAttributeMetadataV2( + input: CustomAttributeInput, + context: MeshContext, +): Promise { + const cacheKey = `customAttributeMetadata-${input.entity_type}-${input.attribute_code}` + const cached = await context.cache.get(cacheKey) + if (cached) return cached + + if (input.entity_type !== 'catalog_product') + throw Error('Only catalog_product is supported at this moment') + + if ( + !('customAttributeMetadataV2' in context.m2.Query) || + typeof context.m2.Query.customAttributeMetadataV2 !== 'function' + ) + throw Error('This field is only available in Magento 2.4.7 and up') + + const attribute = await context.m2.Query.customAttributeMetadataV2({ + context, + key: input, + argsFromKeys: (attributes) => ({ attributes }), + selectionSet: /* GraphQL */ ` + { + items { + __typename + code + label + default_value + entity_type + frontend_class + frontend_input + is_required + is_unique + label + ... on CatalogAttributeMetadata { + apply_to + is_comparable + is_filterable + is_filterable_in_search + is_html_allowed_on_front + is_searchable + is_used_for_price_rules + is_used_for_promo_rules + is_visible_in_advanced_search + is_visible_on_front + is_wysiwyg_enabled + used_in_product_listing + } + options { + label + is_default + value + } + } + } + `, + valuesFromResults: (res, attributes) => + attributes.map((attr) => res.items?.find((v) => v?.code === attr.attribute_code)), + }) + + // Cache for 1 hour + await context.cache.set(cacheKey, attribute, { ttl: 60 * 60 }) + return attribute ?? null +} diff --git a/packages/magento-graphql/mesh/customAttributeV2Resolver.ts b/packages/magento-graphql/mesh/customAttributeV2Resolver.ts new file mode 100644 index 0000000000..1e0d35b690 --- /dev/null +++ b/packages/magento-graphql/mesh/customAttributeV2Resolver.ts @@ -0,0 +1,47 @@ +import fragments from '@graphcommerce/graphql/generated/fragments.json' +import type { MeshContext, ProductInterfaceResolvers, Resolvers } from '@graphcommerce/graphql-mesh' +import { Kind } from 'graphql' +import { CustomAttributeInput, customAttributeMetadataV2 } from './customAttributeMetadataV2' + +type CustomAttributeV2Resolver = Pick, 'custom_attributeV2'> + +const customAttributeV2Resolver: CustomAttributeV2Resolver = { + custom_attributeV2: { + selectionSet: (fieldNode) => ({ + kind: Kind.SELECTION_SET, + selections: (fieldNode.arguments ?? []) + .map((arg) => arg.value) + .filter((value) => value.kind === Kind.STRING) + .map((value) => ({ kind: Kind.FIELD, name: { kind: Kind.NAME, value: value.value } })), + }), + resolve: async (root, { attribute_code: code }, context) => { + const value = String(root[code] ?? '') + const input: CustomAttributeInput = { attribute_code: code, entity_type: 'catalog_product' } + const attribute = await customAttributeMetadataV2(input, context) + + if (!attribute || !value) return null + + if ( + attribute?.frontend_input && + ['SELECT', 'MULTISELECT'].includes(attribute.frontend_input) + ) { + const values = attribute.frontend_input === 'SELECT' ? [value] : value.split(',') + const selected_options = values.map((v) => { + const found = attribute.options?.find((o) => o?.value === v || o?.label === v) + if (!found) return null + return { ...found, __typename: 'AttributeSelectedOption' } + }) + return { __typename: 'AttributeSelectedOptions', code, selected_options, attribute } + } + return { __typename: 'AttributeValue', code, value, attribute } + }, + }, +} + +type ProductTypes = NonNullable>> +const productInterfaceTypes = fragments.possibleTypes.ProductInterface as ProductTypes[] +const resolvers: Resolvers = {} +productInterfaceTypes.forEach((productType) => { + if (!resolvers[productType]) resolvers[productType] = customAttributeV2Resolver +}) +export default resolvers diff --git a/packages/magento-graphql/mesh/resolvers.ts b/packages/magento-graphql/mesh/resolvers.ts deleted file mode 100644 index e09c6e3010..0000000000 --- a/packages/magento-graphql/mesh/resolvers.ts +++ /dev/null @@ -1,144 +0,0 @@ -import fragments from '@graphcommerce/graphql/generated/fragments.json' -import type { - AttributeValueInterfaceResolvers, - CustomAttributeMetadataInterface, - MeshContext, - ProductInterfaceResolvers, - Resolvers, -} from '@graphcommerce/graphql-mesh' -import { Kind } from 'graphql' - -type CustomAttributeInput = { attribute_code: string; entity_type: 'catalog_product' } - -async function customAttributeMetadataV2( - input: CustomAttributeInput, - context: MeshContext, -): Promise { - const cacheKey = `customAttributeMetadata-${input.entity_type}-${input.attribute_code}` - const cached = await context.cache.get(cacheKey) - if (cached) return cached - - if (input.entity_type !== 'catalog_product') - throw Error('Only catalog_product is supported at this moment') - - if ( - !('customAttributeMetadataV2' in context.m2.Query) || - typeof context.m2.Query.customAttributeMetadataV2 !== 'function' - ) - throw Error('This field is only available in Magento 2.4.7 and up') - - const attribute = await context.m2.Query.customAttributeMetadataV2({ - context, - key: input, - argsFromKeys: (attributes) => ({ attributes }), - selectionSet: /* GraphQL */ ` - { - items { - __typename - code - label - default_value - entity_type - frontend_class - frontend_input - is_required - is_unique - label - ... on CatalogAttributeMetadata { - apply_to - is_comparable - is_filterable - is_filterable_in_search - is_html_allowed_on_front - is_searchable - is_used_for_price_rules - is_used_for_promo_rules - is_visible_in_advanced_search - is_visible_on_front - is_wysiwyg_enabled - used_in_product_listing - } - options { - label - is_default - value - } - } - } - `, - valuesFromResults: (res, attributes) => - attributes.map((attr) => res.items?.find((v) => v?.code === attr.attribute_code)), - }) - - // Cache for 1 hour - await context.cache.set(cacheKey, attribute, { ttl: 60 * 60 }) - return attribute ?? null -} - -type CustomAttributeV2Resolver = Pick, 'custom_attributeV2'> - -const customAttributeV2Resolver: CustomAttributeV2Resolver = { - custom_attributeV2: { - selectionSet: (fieldNode) => ({ - kind: Kind.SELECTION_SET, - selections: (fieldNode.arguments ?? []) - .map((arg) => arg.value) - .filter((value) => value.kind === Kind.STRING) - .map((value) => ({ kind: Kind.FIELD, name: { kind: Kind.NAME, value: value.value } })), - }), - resolve: async (root, { attribute_code: code }, context) => { - const value = String(root[code] ?? '') - const input: CustomAttributeInput = { attribute_code: code, entity_type: 'catalog_product' } - const attribute = await customAttributeMetadataV2(input, context) - - if (!attribute || !value) return null - - if ( - attribute?.frontend_input && - ['SELECT', 'MULTISELECT'].includes(attribute.frontend_input) - ) { - const values = attribute.frontend_input === 'SELECT' ? [value] : value.split(',') - const selected_options = values.map((v) => { - const found = attribute.options?.find((o) => o?.value === v || o?.label === v) - if (!found) return null - return { ...found, __typename: 'AttributeSelectedOption' } - }) - return { __typename: 'AttributeSelectedOptions', code, selected_options, attribute } - } - return { __typename: 'AttributeValue', code, value, attribute } - }, - }, -} - -const resolvers: Resolvers = {} - -type ProductTypes = NonNullable>> -const productInterfaceTypes = fragments.possibleTypes.ProductInterface as ProductTypes[] - -productInterfaceTypes.forEach((productType) => { - if (!resolvers[productType]) resolvers[productType] = customAttributeV2Resolver -}) - -const attributeValueResolver: AttributeValueResolver = { - attribute: { - selectionSet: `{ code }`, - resolve: async (root, _, context) => - root.attribute ?? - (await customAttributeMetadataV2( - { attribute_code: root.code, entity_type: 'catalog_product' }, - context, - )), - }, -} - -type AttributeValueResolver = Pick, 'attribute'> -type AttributeValueTypes = NonNullable< - Awaited> -> -const attributeValueTypes = fragments.possibleTypes.AttributeValueInterface as AttributeValueTypes[] - -attributeValueTypes.forEach((attributeValueType) => { - if (!resolvers[attributeValueType]) resolvers[attributeValueType] = attributeValueResolver -}) - -export default resolvers diff --git a/packages/magento-graphql/plugins/meshConfigAttrValue.ts b/packages/magento-graphql/plugins/meshConfigAttrValue.ts index c3f5a77ad6..6cfae2d863 100644 --- a/packages/magento-graphql/plugins/meshConfigAttrValue.ts +++ b/packages/magento-graphql/plugins/meshConfigAttrValue.ts @@ -16,7 +16,8 @@ export const meshConfig: FunctionPlugin = ( ...baseConfig, additionalResolvers: [ ...(baseConfig.additionalResolvers ?? []), - '@graphcommerce/magento-graphql/mesh/resolvers.ts', + '@graphcommerce/magento-graphql/mesh/customAttributeV2Resolver.ts', + '@graphcommerce/magento-graphql/mesh/attributeValueResolver.ts', ], }, graphCommerceConfig, diff --git a/packages/magento-graphql/schema/AttributeValueInterface-attribute.graphqls b/packages/magento-graphql/schema/AttributeValueInterface-attribute.graphqls new file mode 100644 index 0000000000..4e11a19b90 --- /dev/null +++ b/packages/magento-graphql/schema/AttributeValueInterface-attribute.graphqls @@ -0,0 +1,9 @@ +extend interface AttributeValueInterface { + attribute: CustomAttributeMetadataInterface +} +extend type AttributeValue { + attribute: CustomAttributeMetadataInterface +} +extend type AttributeSelectedOptions { + attribute: CustomAttributeMetadataInterface +} diff --git a/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls b/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls index d0786d36a9..6e754b7e88 100644 --- a/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls +++ b/packages/magento-graphql/schema/ProductInterface-custom_attribute.graphqls @@ -1,13 +1,3 @@ -extend interface AttributeValueInterface { - attribute: CustomAttributeMetadataInterface -} -extend type AttributeValue { - attribute: CustomAttributeMetadataInterface -} -extend type AttributeSelectedOptions { - attribute: CustomAttributeMetadataInterface -} - extend interface ProductInterface { """ This is the singular version of the custom_attributesV2 and allows selecting a single attribute option value.