diff --git a/packages/medusa/src/interfaces/index.ts b/packages/medusa/src/interfaces/index.ts index 492e7f056f..b19351aeef 100644 --- a/packages/medusa/src/interfaces/index.ts +++ b/packages/medusa/src/interfaces/index.ts @@ -8,3 +8,4 @@ export * from "./notification-service" export * from "./price-selection-strategy" export * from "./models/base-entity" export * from "./models/soft-deletable-entity" +export * from "./search-service" diff --git a/packages/medusa/src/interfaces/search-service.ts b/packages/medusa/src/interfaces/search-service.ts new file mode 100644 index 0000000000..ac7563cc59 --- /dev/null +++ b/packages/medusa/src/interfaces/search-service.ts @@ -0,0 +1,127 @@ +import { TransactionBaseService } from "./transaction-base-service" +import { SearchService } from "medusa-interfaces" + +export interface ISearchService> { + options: Record + + /** + * Used to create an index + * @param indexName the index name + * @param options the options + * @return returns response from search engine provider + */ + createIndex(indexName: string, options: unknown): unknown + + /** + * Used to get an index + * @param indexName - the index name. + * @return returns response from search engine provider + */ + getIndex(indexName: string): unknown + + /** + * Used to index documents by the search engine provider + * @param indexName the index name + * @param documents documents array to be indexed + * @param type of documents to be added (e.g: products, regions, orders, etc) + * @return returns response from search engine provider + */ + addDocuments(indexName: string, documents: unknown, type: string): unknown + + /** + * Used to replace documents + * @param indexName the index name. + * @param documents array of document objects that will replace existing documents + * @param type type of documents to be replaced (e.g: products, regions, orders, etc) + * @return returns response from search engine provider + */ + replaceDocuments(indexName: string, documents: unknown, type: string): unknown + + /** + * Used to delete document + * @param indexName the index name + * @param document_id the id of the document + * @return returns response from search engine provider + */ + deleteDocument(indexName: string, document_id: string | number): unknown + + /** + * Used to delete all documents + * @param indexName the index name + * @return returns response from search engine provider + */ + deleteAllDocuments(indexName: string): unknown + + /** + * Used to search for a document in an index + * @param indexName the index name + * @param query the search query + * @param options + * - any options passed to the request object other than the query and indexName + * - additionalOptions contain any provider specific options + * @return returns response from search engine provider + */ + search(indexName: string, query: string | null, options: unknown): unknown + + /** + * Used to update the settings of an index + * @param indexName the index name + * @param settings settings object + * @return returns response from search engine provider + */ + updateSettings(indexName: string, settings: unknown): unknown +} + +export abstract class AbstractSearchService< + T extends TransactionBaseService + > + extends TransactionBaseService + implements ISearchService +{ + abstract readonly isDefault + protected readonly options_: Record + + get options(): Record { + return this.options_ + } + + protected constructor(container, options) { + super(container, options) + this.options_ = options + } + + abstract createIndex(indexName: string, options: unknown): unknown + + abstract getIndex(indexName: string): unknown + + abstract addDocuments( + indexName: string, + documents: unknown, + type: string + ): unknown + + abstract replaceDocuments( + indexName: string, + documents: unknown, + type: string + ): unknown + + abstract deleteDocument( + indexName: string, + document_id: string | number + ): unknown + + abstract deleteAllDocuments(indexName: string): unknown + + abstract search( + indexName: string, + query: string | null, + options: unknown + ): unknown + + abstract updateSettings(indexName: string, settings: unknown): unknown +} + +export function isSearchService(obj: unknown): boolean { + return obj instanceof AbstractSearchService || obj instanceof SearchService +} diff --git a/packages/medusa/src/loaders/plugins.ts b/packages/medusa/src/loaders/plugins.ts index 9357a845f7..182c287f78 100644 --- a/packages/medusa/src/loaders/plugins.ts +++ b/packages/medusa/src/loaders/plugins.ts @@ -1,37 +1,37 @@ -import glob from "glob" +import { aliasTo, asClass, asFunction, asValue } from "awilix" import { Express } from "express" -import { EntitySchema } from "typeorm" +import fs from "fs" +import { sync as existsSync } from "fs-exists-cached" +import glob from "glob" +import _ from "lodash" +import { createRequireFromPath } from "medusa-core-utils" import { BaseService as LegacyBaseService, - PaymentService, - FulfillmentService, FileService, + FulfillmentService, OauthService, - SearchService, + PaymentService, } from "medusa-interfaces" -import { createRequireFromPath } from "medusa-core-utils" -import _ from "lodash" import path from "path" -import fs from "fs" -import { asValue, asClass, asFunction, aliasTo } from "awilix" -import { sync as existsSync } from "fs-exists-cached" +import { EntitySchema } from "typeorm" import { AbstractTaxService, + isBatchJobStrategy, isFileService, + isNotificationService, + isPriceSelectionStrategy, + isSearchService, isTaxCalculationStrategy, TransactionBaseService as BaseService, - isNotificationService, - isBatchJobStrategy, - isPriceSelectionStrategy, } from "../interfaces" -import formatRegistrationName from "../utils/format-registration-name" +import { MiddlewareService } from "../services" import { ClassConstructor, ConfigModule, Logger, MedusaContainer, } from "../types/global" -import { MiddlewareService } from "../services" +import formatRegistrationName from "../utils/format-registration-name" import logger from "./logger" type Options = { @@ -418,7 +418,7 @@ export async function registerServices( ), [`fileService`]: aliasTo(name), }) - } else if (loaded.prototype instanceof SearchService) { + } else if (isSearchService(loaded.prototype)) { // Add the service directly to the container in order to make simple // resolution if we already know which search provider we need to use container.register({ diff --git a/packages/medusa/src/loaders/search-index.ts b/packages/medusa/src/loaders/search-index.ts index 21b118b252..a3bd002cf4 100644 --- a/packages/medusa/src/loaders/search-index.ts +++ b/packages/medusa/src/loaders/search-index.ts @@ -1,7 +1,7 @@ import { MedusaContainer } from "../types/global" -import DefaultSearchService from "../services/search" import { Logger } from "../types/global" import { EventBusService } from "../services" +import { AbstractSearchService } from "../interfaces" export const SEARCH_INDEX_EVENT = "SEARCH_INDEX_EVENT" @@ -21,7 +21,8 @@ export default async ({ }: { container: MedusaContainer }): Promise => { - const searchService = container.resolve("searchService") + const searchService = + container.resolve>("searchService") const logger = container.resolve("logger") if (searchService.isDefault) { logger.warn( diff --git a/packages/medusa/src/services/search.js b/packages/medusa/src/services/search.js deleted file mode 100644 index 673faaceb4..0000000000 --- a/packages/medusa/src/services/search.js +++ /dev/null @@ -1,71 +0,0 @@ -import { SearchService } from "medusa-interfaces" - -/** - * Default class that implements SearchService but provides stuv implementation for all methods - * @extends SearchService - */ -class DefaultSearchService extends SearchService { - constructor(container, options) { - super() - - this.isDefault = true - - this.logger_ = container.logger - this.options_ = options - } - - get options() { - return this.options_ - } - - createIndex(indexName, options) { - this.logger_.warn( - "This is an empty method: createIndex must be overridden by a child class" - ) - } - - getIndex(indexName) { - this.logger_.warn( - "This is an empty method: getIndex must be overridden by a child class" - ) - } - - addDocuments(indexName, documents, type) { - this.logger_.warn( - "This is an empty method: addDocuments must be overridden by a child class" - ) - } - - replaceDocuments(indexName, documents, type) { - this.logger_.warn( - "This is an empty method: replaceDocuments must be overridden by a child class" - ) - } - - deleteDocument(indexName, document_id) { - this.logger_.warn( - "This is an empty method: deleteDocument must be overridden by a child class" - ) - } - - deleteAllDocuments(indexName) { - this.logger_.warn( - "This is an empty method: deleteAllDocuments must be overridden by a child class" - ) - } - - search(indexName, query, options) { - this.logger_.warn( - "This is an empty method: search must be overridden a the child class" - ) - return { hits: [] } - } - - updateSettings(indexName, settings) { - this.logger_.warn( - "This is an empty method: updateSettings must be overridden by a child class" - ) - } -} - -export default DefaultSearchService diff --git a/packages/medusa/src/services/search.ts b/packages/medusa/src/services/search.ts new file mode 100644 index 0000000000..c6297e82c3 --- /dev/null +++ b/packages/medusa/src/services/search.ts @@ -0,0 +1,83 @@ +import { EntityManager } from "typeorm" +import { AbstractSearchService } from "../interfaces/search-service" +import { Logger } from "../types/global" + +type InjectedDependencies = { + logger: Logger + manager: EntityManager +} + +export default class DefaultSearchService extends AbstractSearchService { + isDefault = true + + protected manager_: EntityManager + protected transactionManager_: EntityManager | undefined + protected readonly logger_: Logger + protected readonly options_: Record + + constructor({ logger, manager }: InjectedDependencies, options) { + super( + { + logger, + }, + options + ) + + this.options_ = options + this.logger_ = logger + this.manager_ = manager + } + + createIndex(indexName: string, options: unknown): void { + this.logger_.warn( + "This is an empty method: createIndex must be overridden by a child class" + ) + } + + getIndex(indexName: string): void { + this.logger_.warn( + "This is an empty method: getIndex must be overridden by a child class" + ) + } + + addDocuments(indexName: string, documents: unknown, type: string): void { + this.logger_.warn( + "This is an empty method: addDocuments must be overridden by a child class" + ) + } + + replaceDocuments(indexName: string, documents: unknown, type: string): void { + this.logger_.warn( + "This is an empty method: replaceDocuments must be overridden by a child class" + ) + } + + deleteDocument(indexName: string, document_id: string | number): void { + this.logger_.warn( + "This is an empty method: deleteDocument must be overridden by a child class" + ) + } + + deleteAllDocuments(indexName: string): void { + this.logger_.warn( + "This is an empty method: deleteAllDocuments must be overridden by a child class" + ) + } + + search( + indexName: string, + query: unknown, + options: unknown + ): { hits: unknown[] } { + this.logger_.warn( + "This is an empty method: search must be overridden a the child class" + ) + return { hits: [] } + } + + updateSettings(indexName: string, settings: unknown): void { + this.logger_.warn( + "This is an empty method: updateSettings must be overridden by a child class" + ) + } +} diff --git a/packages/medusa/src/subscribers/search-indexing.ts b/packages/medusa/src/subscribers/search-indexing.ts index ad4893606a..2f7f2f31f7 100644 --- a/packages/medusa/src/subscribers/search-indexing.ts +++ b/packages/medusa/src/subscribers/search-indexing.ts @@ -3,17 +3,17 @@ import { SEARCH_INDEX_EVENT } from "../loaders/search-index" import ProductService from "../services/product" import { indexTypes } from "medusa-core-utils" import { Product } from "../models" -import { SearchService } from "../services" +import { ISearchService } from "../interfaces" type InjectedDependencies = { eventBusService: EventBusService - searchService: SearchService + searchService: ISearchService productService: ProductService } class SearchIndexingSubscriber { private readonly eventBusService_: EventBusService - private readonly searchService_: SearchService + private readonly searchService_: ISearchService private readonly productService_: ProductService constructor({ @@ -29,7 +29,7 @@ class SearchIndexingSubscriber { } indexDocuments = async (): Promise => { - const TAKE = this.searchService_?.options?.batch_size ?? 1000 + const TAKE = (this.searchService_?.options?.batch_size as number) ?? 1000 let hasMore = true let lastSeenId = ""