Vue Storefront is now Alokai! Learn More
SearchProducts

SearchProducts

Implements SearchProducts Unified Method.

Source

/* eslint-disable no-secrets/no-secrets */
/* eslint-disable max-statements */
import { defineApi } from "@vsf-enterprise/unified-api-sapcc";
import type { SapccIntegrationContext } from "@vsf-enterprise/sapcc-api";
import {
  type SearchProducts,
  type SearchProductsArgs,
  getNormalizers,
  assignToNormalizerContext,
} from "@vsf-enterprise/unified-api-sapcc/udl";
import { buildFilters, buildProductsResponse, buildQueryString } from "@vsf-enterprise/unified-api-sapcc";


declare module "@vsf-enterprise/unified-api-sapcc" {
  interface SearchProductsExtendedArgs {
    /**
     * Response configuration. List of fields returned in the response body.
     */
    fields?: "BASIC" | "DEFAULT" | "FULL" | string | string[];
    /**
     * Parameter allowing us to pass on a raw query string expected by SAP OCC API.
     *
     * @remarks
     * Useful whenever SAP Commerce Cloud gives us ready-to-use search queries (such as the ones
     * in {@link @vsf-enterprise/sap-commerce-webservices-sdk#FacetValue | FacetValues}). When you use the `query` parameter, other
     * query-building parameters such `filters`, `searchTerm` and `sort` are ignored.
     *
     * @example
     * ```ts
     * const query = ':undefined:allCategories:collections:allCategories:brands:reviewAvgRating:4.4';
     * ```
     */
    query?: string;

    /**
     * The context to be used in the search query.
     */
    searchQueryContext?: string;
  }
}


export const searchProducts = defineApi.searchProducts(async (context, args) => {
  try {
    return await searchProductsByQuery(context, args);
  } catch (error) {
    console.error("/searchProducts", error);
    return {
      products: [],
      pagination: {
        currentPage: 0,
        pageSize: 0,
        totalResults: 0,
        totalPages: 0,
      },
      facets: [],
    };
  }
});

function translateSort(sortBy: SearchProductsArgs["sortBy"]) {
  switch (sortBy) {
    case "relevant": {
      return "relevance";
    }
    case "price-low-to-high": {
      return "price-asc";
    }
    case "price-high-to-low": {
      return "price-desc";
    }
    default: {
      return sortBy;
    }
  }
}

async function searchProductsByQuery(
  context: SapccIntegrationContext,
  { search, sortBy, facets: facetsQuery, category, currentPage = 1, pageSize }: SearchProductsArgs,
): ReturnType<SearchProducts> {
  const filters = buildFilters({ category, facets: facetsQuery });
  const { filterFacets = () => true } = context.config.unified;
  const { normalizePagination, normalizeFacet } = getNormalizers(context);

  const sort = translateSort(sortBy);
  const searchTerm = search;

  const { data: searchProductResponse } = await context.api.getProducts({
    currentPage: Math.max(currentPage - 1, 0),
    pageSize,
    sort,
    query: buildQueryString({ searchTerm, filters, sort }),
  });

  const {
    facets: rawFacets = [],
    products: rawProducts = [],
    pagination: rawPagination = {},
    currentQuery,
  } = searchProductResponse;

  assignToNormalizerContext(context, {
    currentQuery,
  });

  return {
    pagination: normalizePagination(rawPagination),
    facets: rawFacets
      .filter((facet) => filterFacets(facet))
      .map((facet) => normalizeFacet(facet))
      .filter(Boolean),
    ...buildProductsResponse(context, rawProducts),
  };
}