diff --git a/packages/elasticsearch-plugin/e2e/elasticsearch-plugin.e2e-spec.ts b/packages/elasticsearch-plugin/e2e/elasticsearch-plugin.e2e-spec.ts index c0cefa0f7c..f2e7f20215 100644 --- a/packages/elasticsearch-plugin/e2e/elasticsearch-plugin.e2e-spec.ts +++ b/packages/elasticsearch-plugin/e2e/elasticsearch-plugin.e2e-spec.ts @@ -9,6 +9,7 @@ import { mergeConfig, } from '@vendure/core'; import { createTestEnvironment, E2E_DEFAULT_CHANNEL_TOKEN } from '@vendure/testing'; +import { fail } from 'assert'; import gql from 'graphql-tag'; import path from 'path'; @@ -138,6 +139,13 @@ describe('Elasticsearch plugin', () => { return 42; }, }, + hello: { + graphQlType: 'String!', + public: false, + valueFn: args => { + return 'World'; + }, + }, }, searchConfig: { scriptFields: { @@ -1344,6 +1352,29 @@ describe('Elasticsearch plugin', () => { }, }); }); + + it('private mappings', async () => { + const query = `{ + search(input: { take: 1, groupByProduct: true, sort: { name: ASC } }) { + items { + customMappings { + ...on CustomProductMappings { + answer + hello + } + } + } + } + }`; + try { + await shopClient.query(gql(query)); + } catch (error) { + expect(error).toBeDefined(); + expect(error.message).toContain('Cannot query field "hello"'); + return; + } + fail('should not be able to query field "hello"'); + }); }); describe('scriptFields', () => { diff --git a/packages/elasticsearch-plugin/src/api/api-extensions.ts b/packages/elasticsearch-plugin/src/api/api-extensions.ts index 56de082e06..f8bd3482a6 100644 --- a/packages/elasticsearch-plugin/src/api/api-extensions.ts +++ b/packages/elasticsearch-plugin/src/api/api-extensions.ts @@ -44,8 +44,12 @@ export function generateSchemaExtensions(options: ElasticsearchOptions): Documen } function generateCustomMappingTypes(options: ElasticsearchOptions): DocumentNode | undefined { - const productMappings = Object.entries(options.customProductMappings || {}); - const variantMappings = Object.entries(options.customProductVariantMappings || {}); + const productMappings = Object.entries(options.customProductMappings || {}).filter( + ([, value]) => value.public ?? true, + ); + const variantMappings = Object.entries(options.customProductVariantMappings || {}).filter( + ([, value]) => value.public ?? true, + ); const searchInputTypeExtensions = Object.entries(options.extendSearchInputType || {}); const scriptProductFields = Object.entries(options.searchConfig?.scriptFields || {}).filter( ([, scriptField]) => scriptField.context !== 'variant', diff --git a/packages/elasticsearch-plugin/src/options.ts b/packages/elasticsearch-plugin/src/options.ts index e938cfccf5..5727c431fa 100644 --- a/packages/elasticsearch-plugin/src/options.ts +++ b/packages/elasticsearch-plugin/src/options.ts @@ -166,6 +166,10 @@ export interface ElasticsearchOptions { * The `graphQlType` property may be one of `String`, `Int`, `Float`, `Boolean`, `ID` or list * versions thereof (`[String!]` etc) and can be appended with a `!` to indicate non-nullable fields. * + * The `public` (default = `true`) property is used to reveal or hide the property in the GraphQL API schema. + * If this property is set to `false` it's not accessible in the `customMappings` field but it's still getting + * parsed to the elasticsearch index. + * * This config option defines custom mappings which are accessible when the "groupByProduct" * input options is set to `true`. * @@ -178,8 +182,14 @@ export interface ElasticsearchOptions { * }, * reviewRating: { * graphQlType: 'Float', + * public: true, * valueFn: product => (product.customFields as any).reviewRating, * }, + * priority: { + * graphQlType: 'Int!', + * public: false, + * valueFn: product => (product.customFields as any).priority, + * }, * } * ``` * diff --git a/packages/elasticsearch-plugin/src/types.ts b/packages/elasticsearch-plugin/src/types.ts index 6d4b3da713..d1c73cd5c2 100644 --- a/packages/elasticsearch-plugin/src/types.ts +++ b/packages/elasticsearch-plugin/src/types.ts @@ -249,6 +249,7 @@ type GraphQlPermittedReturnType = PrimitiveTypeVariations; type CustomMappingDefinition = { graphQlType: T; + public?: boolean; valueFn: (...args: Args) => R; };