graycoreio/daffodil

View on GitHub
libs/search-product/driver/magento/src/product-search.service.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import {
  Inject,
  Injectable,
} from '@angular/core';
import { Apollo } from 'apollo-angular';
import { DocumentNode } from 'graphql';
import {
  combineLatest,
  Observable,
  throwError,
} from 'rxjs';
import {
  catchError,
  map,
} from 'rxjs/operators';

import { daffApplyRequestsToFilters } from '@daffodil/core';
import {
  magentoProductAddMetadataTypesToAggregates,
  MagentoProductGetFilterTypes,
  MagentoProductGetFilterTypesResponse,
  MagentoProductAppliedFiltersTransformService,
  DAFF_PRODUCT_MAGENTO_EXTRA_PRODUCT_PREVIEW_FRAGMENTS,
  DaffMagentoProductsTransformer,
  DaffProductMagentoDriverConfig,
  MAGENTO_PRODUCT_CONFIG_TOKEN,
  magentoProductCollectionMetadataTransform,
  magentoAppliedSortOptionTransform,
  DAFF_PRODUCT_MAGENTO_EXTRA_PRODUCT_FRAGMENTS,
  DAFF_PRODUCT_MAGENTO_PRODUCT_TRANSFORM,
  DaffMagentoProductTransform,
} from '@daffodil/product/driver/magento';
import {
  DaffSearchResultCollection,
  daffSearchTransformResultsToCollection,
} from '@daffodil/search';
import { DaffSearchDriverOptions } from '@daffodil/search/driver';
import {
  daffTransformProductsToSearchResults,
  DAFF_SEARCH_PRODUCT_RESULT_KIND,
  DaffSearchProductResult,
} from '@daffodil/search-product';
import {
  DaffSearchProductDriverInterface,
  DaffSearchProductDriverOptions,
  DaffSearchProductDriverResponse,
} from '@daffodil/search-product/driver';

import { transformSearchProductMagentoError } from './errors/transform';
import {
  MagentoSearchForProductsResponse,
  MagentoSearchProductIncrementalResponse,
} from './models/public_api';
import {
  daffSearchProductIncrementalQuery,
  productSearch,
} from './queries/public_api';

/**
 * A service for searching products in Magento.
 *
 * @inheritdoc
 */
@Injectable({
  providedIn: 'root',
})
export class DaffSearchProductMagentoDriver implements DaffSearchProductDriverInterface {
  constructor(
    private apollo: Apollo,
    private productFilterRequestsTransformer: MagentoProductAppliedFiltersTransformService,
    @Inject(DAFF_PRODUCT_MAGENTO_EXTRA_PRODUCT_FRAGMENTS) private extraFragments: DocumentNode[],
    @Inject(DAFF_PRODUCT_MAGENTO_EXTRA_PRODUCT_PREVIEW_FRAGMENTS) private extraPreviewFragments: DocumentNode[],
    private magentoProductsTransformers: DaffMagentoProductsTransformer,
    @Inject(MAGENTO_PRODUCT_CONFIG_TOKEN) private config: DaffProductMagentoDriverConfig,
    @Inject(DAFF_PRODUCT_MAGENTO_PRODUCT_TRANSFORM) private magentoProductsTransform: DaffMagentoProductTransform,
  ) {}

  readonly kind = DAFF_SEARCH_PRODUCT_RESULT_KIND;

  private getVariables(options: DaffSearchProductDriverOptions = {}) {
    const queryVariables = {
      filter: this.productFilterRequestsTransformer.transform(options.filterRequests),
    };
    if (options.pageSize || options.limit) {
      queryVariables['pageSize'] = options.pageSize || options.limit;
    }
    if (options.currentPage) {
      queryVariables['currentPage'] = options.currentPage;
    }
    if (options.appliedSortOption && options.appliedSortDirection) {
      queryVariables['sort'] = magentoAppliedSortOptionTransform(options.appliedSortOption, options.appliedSortDirection);
    }
    return queryVariables;
  }

  search(query: string, options: DaffSearchProductDriverOptions = {}): Observable<DaffSearchProductDriverResponse> {
    const queryVariables = this.getVariables(options);

    return combineLatest([
      this.apollo.query<MagentoSearchForProductsResponse>({
        query: productSearch([
          ...this.extraPreviewFragments,
          ...this.extraFragments,
        ]),
        variables: {
          ...queryVariables,
          search: query,
        },
      }),
      this.apollo.query<MagentoProductGetFilterTypesResponse>({
        query: MagentoProductGetFilterTypes,
      }),
    ]).pipe(
      map(([products, filters]) => ({
        products: products.data.products.items,
        filters: magentoProductAddMetadataTypesToAggregates(
          filters.data.__type.inputFields,
          products.data.products.aggregations,
        ),
        sortFields: products.data.products.sort_fields,
        pageInfo: products.data.products.page_info,
        count: products.data.products.total_count,
      })),
      map(({ products, filters, sortFields, pageInfo, count }) => ({
        products: daffSearchTransformResultsToCollection(daffTransformProductsToSearchResults(
          products.map((product) => this.magentoProductsTransform(product, this.config.baseMediaUrl)),
        )),
        metadata: magentoProductCollectionMetadataTransform(
          filters,
          pageInfo,
          sortFields,
          products,
          count,
          options.appliedSortOption,
          options.appliedSortDirection,
        ),
      })),
      map(({ products, metadata }) => ({
        collection: products,
        metadata: {
          ...metadata,
          filters: daffApplyRequestsToFilters(options.filterRequests || [], metadata.filters),
        },
      })),
      catchError(err => throwError(() => transformSearchProductMagentoError(err))),
    );
  }

  incremental(query: string, options: DaffSearchProductDriverOptions = {}): Observable<DaffSearchResultCollection<DaffSearchProductResult>> {
    return this.apollo.query<MagentoSearchProductIncrementalResponse>({
      query: daffSearchProductIncrementalQuery(this.extraPreviewFragments),
      variables: {
        search: query,
        pageSize: options.pageSize || options.limit,
      },
    }).pipe(
      map(({ data }) => daffSearchTransformResultsToCollection(daffTransformProductsToSearchResults(
        this.magentoProductsTransformers.transformManyMagentoProducts(data.products.items, this.config.baseMediaUrl),
      ))),
      catchError(err => throwError(() => transformSearchProductMagentoError(err))),
    );
  }
}