Selaa lähdekoodia

feat(core): Add asset focal point data to SearchResult type

Relates to #93

BREAKING CHANGE: The `search` query's `SearchResult` type has had two properties deprecated: `productPreview` and `productVariantPreview`. They are replaced by `productAsset.preview` and `productVariantAsset.preview respectively`. The deprecated properties still work but will be removed from a future release.
Michael Bromley 6 vuotta sitten
vanhempi
sitoutus
f717fb33f1

+ 9 - 0
packages/common/src/generated-shop-types.ts

@@ -2044,9 +2044,11 @@ export type SearchResult = {
     productId: Scalars['ID'];
     productName: Scalars['String'];
     productPreview: Scalars['String'];
+    productAsset?: Maybe<SearchResultAsset>;
     productVariantId: Scalars['ID'];
     productVariantName: Scalars['String'];
     productVariantPreview: Scalars['String'];
+    productVariantAsset?: Maybe<SearchResultAsset>;
     price: SearchResultPrice;
     priceWithTax: SearchResultPrice;
     currencyCode: CurrencyCode;
@@ -2059,6 +2061,13 @@ export type SearchResult = {
     score: Scalars['Float'];
 };
 
+export type SearchResultAsset = {
+    __typename?: 'SearchResultAsset';
+    id: Scalars['ID'];
+    preview: Scalars['String'];
+    focalPoint?: Maybe<Coordinate>;
+};
+
 /** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
 

+ 9 - 0
packages/common/src/generated-types.ts

@@ -3114,9 +3114,11 @@ export type SearchResult = {
   productId: Scalars['ID'],
   productName: Scalars['String'],
   productPreview: Scalars['String'],
+  productAsset?: Maybe<SearchResultAsset>,
   productVariantId: Scalars['ID'],
   productVariantName: Scalars['String'],
   productVariantPreview: Scalars['String'],
+  productVariantAsset?: Maybe<SearchResultAsset>,
   price: SearchResultPrice,
   priceWithTax: SearchResultPrice,
   currencyCode: CurrencyCode,
@@ -3129,6 +3131,13 @@ export type SearchResult = {
   score: Scalars['Float'],
 };
 
+export type SearchResultAsset = {
+  __typename?: 'SearchResultAsset',
+  id: Scalars['ID'],
+  preview: Scalars['String'],
+  focalPoint?: Maybe<Coordinate>,
+};
+
 /** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
 

+ 1 - 15
packages/core/e2e/asset.e2e-spec.ts

@@ -1,5 +1,4 @@
 import { omit } from '@vendure/common/lib/omit';
-import { pick } from '@vendure/common/lib/pick';
 import { createTestEnvironment } from '@vendure/testing';
 import gql from 'graphql-tag';
 import path from 'path';
@@ -15,7 +14,7 @@ import {
     SortOrder,
     UpdateAsset,
 } from './graphql/generated-e2e-admin-types';
-import { GET_ASSET_LIST } from './graphql/shared-definitions';
+import { GET_ASSET_LIST, UPDATE_ASSET } from './graphql/shared-definitions';
 
 describe('Asset resolver', () => {
     const { server, adminClient } = createTestEnvironment(testConfig);
@@ -213,16 +212,3 @@ export const CREATE_ASSETS = gql`
     }
     ${ASSET_FRAGMENT}
 `;
-
-export const UPDATE_ASSET = gql`
-    mutation UpdateAsset($input: UpdateAssetInput!) {
-        updateAsset(input: $input) {
-            ...Asset
-            focalPoint {
-                x
-                y
-            }
-        }
-    }
-    ${ASSET_FRAGMENT}
-`;

+ 68 - 0
packages/core/e2e/default-search-plugin.e2e-spec.ts

@@ -1,3 +1,4 @@
+/* tslint:disable:no-non-null-assertion */
 import { pick } from '@vendure/common/lib/pick';
 import { DefaultSearchPlugin, mergeConfig } from '@vendure/core';
 import { facetValueCollectionFilter } from '@vendure/core/dist/config/collection/default-collection-filters';
@@ -19,9 +20,11 @@ import {
     LanguageCode,
     RemoveProductsFromChannel,
     SearchFacetValues,
+    SearchGetAssets,
     SearchGetPrices,
     SearchInput,
     SortOrder,
+    UpdateAsset,
     UpdateCollection,
     UpdateProduct,
     UpdateProductVariants,
@@ -36,6 +39,7 @@ import {
     DELETE_PRODUCT,
     DELETE_PRODUCT_VARIANT,
     REMOVE_PRODUCT_FROM_CHANNEL,
+    UPDATE_ASSET,
     UPDATE_COLLECTION,
     UPDATE_PRODUCT,
     UPDATE_PRODUCT_VARIANTS,
@@ -570,6 +574,41 @@ describe('Default search plugin', () => {
                 ]);
             });
 
+            it('updates index when asset focalPoint is changed', async () => {
+                function doSearch() {
+                    return adminClient.query<SearchGetAssets.Query, SearchGetAssets.Variables>(
+                        SEARCH_GET_ASSETS,
+                        {
+                            input: {
+                                term: 'laptop',
+                                take: 1,
+                            },
+                        },
+                    );
+                }
+                const { search: search1 } = await doSearch();
+
+                expect(search1.items[0].productAsset!.id).toBe('T_1');
+                expect(search1.items[0].productAsset!.focalPoint).toBeNull();
+
+                await adminClient.query<UpdateAsset.Mutation, UpdateAsset.Variables>(UPDATE_ASSET, {
+                    input: {
+                        id: 'T_1',
+                        focalPoint: {
+                            x: 0.42,
+                            y: 0.42,
+                        },
+                    },
+                });
+
+                await awaitRunningJobs(adminClient);
+
+                const { search: search2 } = await doSearch();
+
+                expect(search2.items[0].productAsset!.id).toBe('T_1');
+                expect(search2.items[0].productAsset!.focalPoint).toEqual({ x: 0.42, y: 0.42 });
+            });
+
             it('returns enabled field when not grouped', async () => {
                 const result = await doAdminSearchQuery({ groupByProduct: false, take: 3 });
                 expect(result.search.items.map(pick(['productVariantId', 'enabled']))).toEqual([
@@ -719,6 +758,35 @@ export const SEARCH_GET_FACET_VALUES = gql`
     }
 `;
 
+export const SEARCH_GET_ASSETS = gql`
+    query SearchGetAssets($input: SearchInput!) {
+        search(input: $input) {
+            totalItems
+            items {
+                productId
+                productName
+                productVariantName
+                productAsset {
+                    id
+                    preview
+                    focalPoint {
+                        x
+                        y
+                    }
+                }
+                productVariantAsset {
+                    id
+                    preview
+                    focalPoint {
+                        x
+                        y
+                    }
+                }
+            }
+        }
+    }
+`;
+
 export const SEARCH_GET_PRICES = gql`
     query SearchGetPrices($input: SearchInput!) {
         search(input: $input) {

+ 83 - 17
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -3005,9 +3005,11 @@ export type SearchResult = {
     productId: Scalars['ID'];
     productName: Scalars['String'];
     productPreview: Scalars['String'];
+    productAsset?: Maybe<SearchResultAsset>;
     productVariantId: Scalars['ID'];
     productVariantName: Scalars['String'];
     productVariantPreview: Scalars['String'];
+    productVariantAsset?: Maybe<SearchResultAsset>;
     price: SearchResultPrice;
     priceWithTax: SearchResultPrice;
     currencyCode: CurrencyCode;
@@ -3020,6 +3022,13 @@ export type SearchResult = {
     score: Scalars['Float'];
 };
 
+export type SearchResultAsset = {
+    __typename?: 'SearchResultAsset';
+    id: Scalars['ID'];
+    preview: Scalars['String'];
+    focalPoint?: Maybe<Coordinate>;
+};
+
 /** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
 
@@ -3512,16 +3521,6 @@ export type CreateAssetsMutation = { __typename?: 'Mutation' } & {
     >;
 };
 
-export type UpdateAssetMutationVariables = {
-    input: UpdateAssetInput;
-};
-
-export type UpdateAssetMutation = { __typename?: 'Mutation' } & {
-    updateAsset: { __typename?: 'Asset' } & {
-        focalPoint: Maybe<{ __typename?: 'Coordinate' } & Pick<Coordinate, 'x' | 'y'>>;
-    } & AssetFragment;
-};
-
 export type CanCreateCustomerMutationVariables = {
     input: CreateCustomerInput;
 };
@@ -3848,6 +3847,42 @@ export type SearchFacetValuesQuery = { __typename?: 'Query' } & {
         };
 };
 
+export type SearchGetAssetsQueryVariables = {
+    input: SearchInput;
+};
+
+export type SearchGetAssetsQuery = { __typename?: 'Query' } & {
+    search: { __typename?: 'SearchResponse' } & Pick<SearchResponse, 'totalItems'> & {
+            items: Array<
+                { __typename?: 'SearchResult' } & Pick<
+                    SearchResult,
+                    'productId' | 'productName' | 'productVariantName'
+                > & {
+                        productAsset: Maybe<
+                            { __typename?: 'SearchResultAsset' } & Pick<
+                                SearchResultAsset,
+                                'id' | 'preview'
+                            > & {
+                                    focalPoint: Maybe<
+                                        { __typename?: 'Coordinate' } & Pick<Coordinate, 'x' | 'y'>
+                                    >;
+                                }
+                        >;
+                        productVariantAsset: Maybe<
+                            { __typename?: 'SearchResultAsset' } & Pick<
+                                SearchResultAsset,
+                                'id' | 'preview'
+                            > & {
+                                    focalPoint: Maybe<
+                                        { __typename?: 'Coordinate' } & Pick<Coordinate, 'x' | 'y'>
+                                    >;
+                                }
+                        >;
+                    }
+            >;
+        };
+};
+
 export type SearchGetPricesQueryVariables = {
     input: SearchInput;
 };
@@ -4562,6 +4597,16 @@ export type RemoveProductsFromChannelMutation = { __typename?: 'Mutation' } & {
     removeProductsFromChannel: Array<{ __typename?: 'Product' } & ProductWithVariantsFragment>;
 };
 
+export type UpdateAssetMutationVariables = {
+    input: UpdateAssetInput;
+};
+
+export type UpdateAssetMutation = { __typename?: 'Mutation' } & {
+    updateAsset: { __typename?: 'Asset' } & {
+        focalPoint: Maybe<{ __typename?: 'Coordinate' } & Pick<Coordinate, 'x' | 'y'>>;
+    } & AssetFragment;
+};
+
 export type UpdateOptionGroupMutationVariables = {
     input: UpdateProductOptionGroupInput;
 };
@@ -5332,13 +5377,6 @@ export namespace CreateAssets {
     >;
 }
 
-export namespace UpdateAsset {
-    export type Variables = UpdateAssetMutationVariables;
-    export type Mutation = UpdateAssetMutation;
-    export type UpdateAsset = AssetFragment;
-    export type FocalPoint = NonNullable<UpdateAssetMutation['updateAsset']['focalPoint']>;
-}
-
 export namespace CanCreateCustomer {
     export type Variables = CanCreateCustomerMutationVariables;
     export type Mutation = CanCreateCustomerMutation;
@@ -5559,6 +5597,27 @@ export namespace SearchFacetValues {
     export type FacetValue = (NonNullable<SearchFacetValuesQuery['search']['facetValues'][0]>)['facetValue'];
 }
 
+export namespace SearchGetAssets {
+    export type Variables = SearchGetAssetsQueryVariables;
+    export type Query = SearchGetAssetsQuery;
+    export type Search = SearchGetAssetsQuery['search'];
+    export type Items = NonNullable<SearchGetAssetsQuery['search']['items'][0]>;
+    export type ProductAsset = NonNullable<
+        (NonNullable<SearchGetAssetsQuery['search']['items'][0]>)['productAsset']
+    >;
+    export type FocalPoint = NonNullable<
+        (NonNullable<(NonNullable<SearchGetAssetsQuery['search']['items'][0]>)['productAsset']>)['focalPoint']
+    >;
+    export type ProductVariantAsset = NonNullable<
+        (NonNullable<SearchGetAssetsQuery['search']['items'][0]>)['productVariantAsset']
+    >;
+    export type _FocalPoint = NonNullable<
+        (NonNullable<
+            (NonNullable<SearchGetAssetsQuery['search']['items'][0]>)['productVariantAsset']
+        >)['focalPoint']
+    >;
+}
+
 export namespace SearchGetPrices {
     export type Variables = SearchGetPricesQueryVariables;
     export type Query = SearchGetPricesQuery;
@@ -6037,6 +6096,13 @@ export namespace RemoveProductsFromChannel {
     export type RemoveProductsFromChannel = ProductWithVariantsFragment;
 }
 
+export namespace UpdateAsset {
+    export type Variables = UpdateAssetMutationVariables;
+    export type Mutation = UpdateAssetMutation;
+    export type UpdateAsset = AssetFragment;
+    export type FocalPoint = NonNullable<UpdateAssetMutation['updateAsset']['focalPoint']>;
+}
+
 export namespace UpdateOptionGroup {
     export type Variables = UpdateOptionGroupMutationVariables;
     export type Mutation = UpdateOptionGroupMutation;

+ 9 - 0
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -2044,9 +2044,11 @@ export type SearchResult = {
     productId: Scalars['ID'];
     productName: Scalars['String'];
     productPreview: Scalars['String'];
+    productAsset?: Maybe<SearchResultAsset>;
     productVariantId: Scalars['ID'];
     productVariantName: Scalars['String'];
     productVariantPreview: Scalars['String'];
+    productVariantAsset?: Maybe<SearchResultAsset>;
     price: SearchResultPrice;
     priceWithTax: SearchResultPrice;
     currencyCode: CurrencyCode;
@@ -2059,6 +2061,13 @@ export type SearchResult = {
     score: Scalars['Float'];
 };
 
+export type SearchResultAsset = {
+    __typename?: 'SearchResultAsset';
+    id: Scalars['ID'];
+    preview: Scalars['String'];
+    focalPoint?: Maybe<Coordinate>;
+};
+
 /** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
 

+ 12 - 0
packages/core/e2e/graphql/shared-definitions.ts

@@ -337,3 +337,15 @@ export const REMOVE_PRODUCT_FROM_CHANNEL = gql`
     }
     ${PRODUCT_WITH_VARIANTS_FRAGMENT}
 `;
+export const UPDATE_ASSET = gql`
+    mutation UpdateAsset($input: UpdateAssetInput!) {
+        updateAsset(input: $input) {
+            ...Asset
+            focalPoint {
+                x
+                y
+            }
+        }
+    }
+    ${ASSET_FRAGMENT}
+`;

+ 7 - 4
packages/core/src/api/middleware/asset-interceptor-plugin.ts

@@ -63,11 +63,14 @@ export class AssetInterceptorPlugin implements ApolloServerPlugin {
             const isSearchResultType = type && type.name === 'SearchResult';
             if (isSearchResultType) {
                 if (value && !Array.isArray(value)) {
-                    if (value.productPreview) {
-                        value.productPreview = toAbsoluteUrl(request, value.productPreview);
+                    if (value.productAsset) {
+                        value.productAsset.preview = toAbsoluteUrl(request, value.productAsset.preview);
                     }
-                    if (value.productVariantPreview) {
-                        value.productVariantPreview = toAbsoluteUrl(request, value.productVariantPreview);
+                    if (value.productVariantAsset) {
+                        value.productVariantAsset.preview = toAbsoluteUrl(
+                            request,
+                            value.productVariantAsset.preview,
+                        );
                     }
                 }
             }

+ 10 - 2
packages/core/src/api/schema/type/product-search.type.graphql

@@ -17,15 +17,23 @@ type FacetValueResult {
     count: Int!
 }
 
+type SearchResultAsset {
+    id: ID!
+    preview: String!
+    focalPoint: Coordinate
+}
+
 type SearchResult {
     sku: String!
     slug: String!
     productId: ID!
     productName: String!
-    productPreview: String!
+    productPreview: String! @deprecated(reason: "Use `productAsset.preview` instead")
+    productAsset: SearchResultAsset
     productVariantId: ID!
     productVariantName: String!
-    productVariantPreview: String!
+    productVariantPreview: String! @deprecated(reason: "Use `productVariantAsset.preview` instead")
+    productVariantAsset: SearchResultAsset
     price: SearchResultPrice!
     priceWithTax: SearchResultPrice!
     currencyCode: CurrencyCode!

+ 6 - 0
packages/core/src/plugin/default-search-plugin/default-search-plugin.ts

@@ -4,6 +4,7 @@ import { buffer, debounceTime, filter, map } from 'rxjs/operators';
 
 import { idsAreEqual } from '../../common/utils';
 import { EventBus } from '../../event-bus/event-bus';
+import { AssetEvent } from '../../event-bus/events/asset-event';
 import { CollectionModificationEvent } from '../../event-bus/events/collection-modification-event';
 import { ProductChannelEvent } from '../../event-bus/events/product-channel-event';
 import { ProductEvent } from '../../event-bus/events/product-event';
@@ -80,6 +81,11 @@ export class DefaultSearchPlugin implements OnVendureBootstrap {
                 return this.searchIndexService.updateVariants(event.ctx, event.variants).start();
             }
         });
+        this.eventBus.ofType(AssetEvent).subscribe(event => {
+            if (event.type === 'updated') {
+                return this.searchIndexService.updateAsset(event.ctx, event.asset).start();
+            }
+        });
         this.eventBus.ofType(ProductChannelEvent).subscribe(event => {
             if (event.type === 'assigned') {
                 return this.searchIndexService

+ 25 - 0
packages/core/src/plugin/default-search-plugin/indexer/indexer.controller.ts

@@ -24,6 +24,7 @@ import {
     DeleteVariantMessage,
     ReindexMessage,
     RemoveProductFromChannelMessage,
+    UpdateAssetMessage,
     UpdateProductMessage,
     UpdateVariantMessage,
     UpdateVariantsByIdMessage,
@@ -189,6 +190,24 @@ export class IndexerController {
         });
     }
 
+    @MessagePattern(UpdateAssetMessage.pattern)
+    updateAsset(data: UpdateAssetMessage['data']): Observable<UpdateAssetMessage['response']> {
+        return asyncObservable(async () => {
+            const id = data.asset.id;
+            function getFocalPoint(point?: { x: number; y: number }) {
+                return point && point.x && point.y ? point : null;
+            }
+            const focalPoint = getFocalPoint(data.asset.focalPoint);
+            await this.connection
+                .getRepository(SearchIndexItem)
+                .update({ productAssetId: id }, { productPreviewFocalPoint: focalPoint });
+            await this.connection
+                .getRepository(SearchIndexItem)
+                .update({ productVariantAssetId: id }, { productVariantPreviewFocalPoint: focalPoint });
+            return true;
+        });
+    }
+
     private async updateProductInChannel(
         ctx: RequestContext,
         productId: ID,
@@ -286,6 +305,12 @@ export class IndexerController {
                     productName: v.product.name,
                     description: v.product.description,
                     productVariantName: v.name,
+                    productAssetId: v.product.featuredAsset ? v.product.featuredAsset.id : null,
+                    productPreviewFocalPoint: v.product.featuredAsset
+                        ? v.product.featuredAsset.focalPoint
+                        : null,
+                    productVariantPreviewFocalPoint: v.featuredAsset ? v.featuredAsset.focalPoint : null,
+                    productVariantAssetId: v.featuredAsset ? v.featuredAsset.id : null,
                     productPreview: v.product.featuredAsset ? v.product.featuredAsset.preview : '',
                     productVariantPreview: v.featuredAsset ? v.featuredAsset.preview : '',
                     channelIds: v.product.channels.map(c => c.id as string),

+ 9 - 0
packages/core/src/plugin/default-search-plugin/indexer/search-index.service.ts

@@ -3,6 +3,7 @@ import { ID } from '@vendure/common/lib/shared-types';
 
 import { RequestContext } from '../../../api/common/request-context';
 import { Logger } from '../../../config/logger/vendure-logger';
+import { Asset } from '../../../entity/asset/asset.entity';
 import { ProductVariant } from '../../../entity/product-variant/product-variant.entity';
 import { Product } from '../../../entity/product/product.entity';
 import { Job } from '../../../service/helpers/job-manager/job';
@@ -16,6 +17,7 @@ import {
     ReindexMessage,
     ReindexMessageResponse,
     RemoveProductFromChannelMessage,
+    UpdateAssetMessage,
     UpdateProductMessage,
     UpdateVariantMessage,
     UpdateVariantsByIdMessage,
@@ -88,6 +90,13 @@ export class SearchIndexService {
         });
     }
 
+    updateAsset(ctx: RequestContext, asset: Asset) {
+        return this.createShortWorkerJob(new UpdateAssetMessage({ ctx, asset }), {
+            entity: 'Asset',
+            id: asset.id,
+        });
+    }
+
     assignProductToChannel(ctx: RequestContext, productId: ID, channelId: ID) {
         const data = { ctx, productId, channelId };
         return this.createShortWorkerJob(new AssignProductToChannelMessage(data), {

+ 12 - 2
packages/core/src/plugin/default-search-plugin/search-index-item.entity.ts

@@ -53,8 +53,6 @@ export class SearchIndexItem {
     @Column()
     priceWithTax: number;
 
-    currencyCode: CurrencyCode;
-
     @Column('simple-array')
     facetIds: string[];
 
@@ -70,6 +68,18 @@ export class SearchIndexItem {
     @Column()
     productPreview: string;
 
+    @Column('simple-json', { nullable: true })
+    productPreviewFocalPoint?: { x: number; y: number } | null;
+
     @Column()
     productVariantPreview: string;
+
+    @Column('simple-json', { nullable: true })
+    productVariantPreviewFocalPoint?: { x: number; y: number } | null;
+
+    @EntityId({ nullable: true })
+    productAssetId: ID | null;
+
+    @EntityId({ nullable: true })
+    productVariantAssetId: ID | null;
 }

+ 4 - 0
packages/core/src/plugin/default-search-plugin/search-strategy/postgres-search-strategy.ts

@@ -180,8 +180,12 @@ export class PostgresSearchStrategy implements SearchStrategy {
             'facetValueIds',
             'collectionIds',
             'channelIds',
+            'productAssetId',
             'productPreview',
+            'productPreviewFocalPoint',
+            'productVariantAssetId',
             'productVariantPreview',
+            'productVariantPreviewFocalPoint',
         ]
             .map(col => {
                 const qualifiedName = `si.${col}`;

+ 36 - 1
packages/core/src/plugin/default-search-plugin/search-strategy/search-strategy-utils.ts

@@ -1,4 +1,11 @@
-import { CurrencyCode, PriceRange, SearchResult, SinglePrice } from '@vendure/common/lib/generated-types';
+import {
+    Coordinate,
+    CurrencyCode,
+    PriceRange,
+    SearchResult,
+    SearchResultAsset,
+    SinglePrice,
+} from '@vendure/common/lib/generated-types';
 import { ID } from '@vendure/common/lib/shared-types';
 import { unique } from '@vendure/common/lib/unique';
 
@@ -15,6 +22,21 @@ export function mapToSearchResult(raw: any, currencyCode: CurrencyCode): SearchR
             ? ({ min: raw.minPriceWithTax, max: raw.maxPriceWithTax } as PriceRange)
             : ({ value: raw.si_priceWithTax } as SinglePrice);
 
+    const productAsset: SearchResultAsset | null = !raw.si_productAssetId
+        ? null
+        : {
+              id: raw.si_productAssetId,
+              preview: raw.si_productPreview,
+              focalPoint: parseFocalPoint(raw.si_productPreviewFocalPoint),
+          };
+    const productVariantAsset: SearchResultAsset | null = !raw.si_productVariantAssetId
+        ? null
+        : {
+              id: raw.si_productVariantAssetId,
+              preview: raw.si_productVariantPreview,
+              focalPoint: parseFocalPoint(raw.si_productVariantPreviewFocalPoint),
+          };
+
     const enabled = raw.productEnabled != null ? !!Number(raw.productEnabled) : raw.si_enabled;
     return {
         sku: raw.si_sku,
@@ -32,7 +54,9 @@ export function mapToSearchResult(raw: any, currencyCode: CurrencyCode): SearchR
         facetValueIds: raw.si_facetValueIds.split(',').map((x: string) => x.trim()),
         collectionIds: raw.si_collectionIds.split(',').map((x: string) => x.trim()),
         channelIds: raw.si_channelIds.split(',').map((x: string) => x.trim()),
+        productAsset,
         productPreview: raw.si_productPreview,
+        productVariantAsset,
         productVariantPreview: raw.si_productVariantPreview,
         score: raw.score || 0,
     };
@@ -54,3 +78,14 @@ export function createFacetIdCountMap(facetValuesResult: Array<{ facetValues: st
     }
     return result;
 }
+
+function parseFocalPoint(focalPoint: any): Coordinate | null {
+    if (focalPoint && typeof focalPoint === 'string') {
+        try {
+            return JSON.parse(focalPoint);
+        } catch (e) {
+            // fall though
+        }
+    }
+    return null;
+}

+ 8 - 0
packages/core/src/plugin/default-search-plugin/types.ts

@@ -1,6 +1,7 @@
 import { ID } from '@vendure/common/lib/shared-types';
 
 import { RequestContext } from '../../api/common/request-context';
+import { Asset } from '../../entity/asset/asset.entity';
 import { WorkerMessage } from '../../worker/types';
 
 export interface ReindexMessageResponse {
@@ -23,6 +24,10 @@ export interface UpdateVariantsByIdMessageData {
     ctx: RequestContext;
     ids: ID[];
 }
+export interface UpdateAssetMessageData {
+    ctx: RequestContext;
+    asset: Asset;
+}
 
 export interface ProductChannelMessageData {
     ctx: RequestContext;
@@ -57,3 +62,6 @@ export class AssignProductToChannelMessage extends WorkerMessage<ProductChannelM
 export class RemoveProductFromChannelMessage extends WorkerMessage<ProductChannelMessageData, boolean> {
     static readonly pattern = 'RemoveProductFromChannel';
 }
+export class UpdateAssetMessage extends WorkerMessage<UpdateAssetMessageData, boolean> {
+    static readonly pattern = 'UpdateAsset';
+}

+ 9 - 0
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -3005,9 +3005,11 @@ export type SearchResult = {
     productId: Scalars['ID'];
     productName: Scalars['String'];
     productPreview: Scalars['String'];
+    productAsset?: Maybe<SearchResultAsset>;
     productVariantId: Scalars['ID'];
     productVariantName: Scalars['String'];
     productVariantPreview: Scalars['String'];
+    productVariantAsset?: Maybe<SearchResultAsset>;
     price: SearchResultPrice;
     priceWithTax: SearchResultPrice;
     currencyCode: CurrencyCode;
@@ -3020,6 +3022,13 @@ export type SearchResult = {
     score: Scalars['Float'];
 };
 
+export type SearchResultAsset = {
+    __typename?: 'SearchResultAsset';
+    id: Scalars['ID'];
+    preview: Scalars['String'];
+    focalPoint?: Maybe<Coordinate>;
+};
+
 /** The price of a search result product, either as a range or as a single price */
 export type SearchResultPrice = PriceRange | SinglePrice;
 

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
schema-shop.json


Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä