diff --git a/package-lock.json b/package-lock.json index 4734f24ce..8b80c1a2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,15 @@ "dependencies": { "@elastic/elasticsearch": "7.12.0", "@golevelup/nestjs-rabbitmq": "3.4.0", +<<<<<<< HEAD "@multiversx/sdk-core": "^11.1.3", "@multiversx/sdk-native-auth-server": "^1.0.5", "@multiversx/sdk-nestjs": "^0.3.4", +======= + "@multiversx/sdk-core": "11.2.0", + "@multiversx/sdk-native-auth-server": "^0.2.5", + "@multiversx/sdk-nestjs": "0.3.7", +>>>>>>> 24e17812 (Fix asset likes repository integration) "@multiversx/sdk-network-providers": "^1.2.1", "@nestjs/apollo": "^10.1.7", "@nestjs/common": "9.1.4", @@ -3109,9 +3115,9 @@ } }, "node_modules/@multiversx/sdk-nestjs": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-nestjs/-/sdk-nestjs-0.3.4.tgz", - "integrity": "sha512-d9rCswN6lYnbdCyNqagzhVDtv6A8H67r3not47p7Zz1O7ohLwXy+bhnOI0CPlVsjSc9Fnt4LH67xvw8IgniOhQ==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-nestjs/-/sdk-nestjs-0.3.7.tgz", + "integrity": "sha512-BSFFA6hpu734oAY5DyIL89XHI/NB+BOUFftF7y++sdgMGpsVcdySGnJxqQRF0HpaGqYujVwRWJcjmNPGxiwppg==", "dependencies": { "@golevelup/nestjs-rabbitmq": "^3.0.0", "@multiversx/sdk-native-auth-client": "^0.1.8", @@ -16732,9 +16738,9 @@ } }, "@multiversx/sdk-nestjs": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-nestjs/-/sdk-nestjs-0.3.4.tgz", - "integrity": "sha512-d9rCswN6lYnbdCyNqagzhVDtv6A8H67r3not47p7Zz1O7ohLwXy+bhnOI0CPlVsjSc9Fnt4LH67xvw8IgniOhQ==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-nestjs/-/sdk-nestjs-0.3.7.tgz", + "integrity": "sha512-BSFFA6hpu734oAY5DyIL89XHI/NB+BOUFftF7y++sdgMGpsVcdySGnJxqQRF0HpaGqYujVwRWJcjmNPGxiwppg==", "requires": { "@golevelup/nestjs-rabbitmq": "^3.0.0", "@multiversx/sdk-native-auth-client": "^0.1.8", diff --git a/package.json b/package.json index 07e902c34..fb61b5dc8 100644 --- a/package.json +++ b/package.json @@ -29,10 +29,10 @@ "dependencies": { "@elastic/elasticsearch": "7.12.0", "@golevelup/nestjs-rabbitmq": "3.4.0", - "@multiversx/sdk-core": "^11.1.3", + "@multiversx/sdk-core": "11.2.0", "@multiversx/sdk-native-auth-server": "1.0.5", + "@multiversx/sdk-nestjs": "0.3.7", "@multiversx/sdk-network-providers": "^1.2.1", - "@multiversx/sdk-nestjs": "^0.3.4", "@nestjs/apollo": "^10.1.7", "@nestjs/common": "9.1.4", "@nestjs/config": "2.3.0", diff --git a/src/common/persistence/persistence.module.ts b/src/common/persistence/persistence.module.ts index e16b360d9..9f6483837 100644 --- a/src/common/persistence/persistence.module.ts +++ b/src/common/persistence/persistence.module.ts @@ -1,7 +1,6 @@ import { Global, Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AccountStatsRepository } from 'src/db/account-stats/account-stats.repository'; -import { AssetsLikesRepository } from 'src/db/assets/assets-likes.repository'; import { AuctionEntity } from 'src/db/auctions/auction.entity'; import { AuctionsRepository } from 'src/db/auctions/auctions.repository'; import { TagsRepository } from 'src/db/auctions/tags.repository'; @@ -27,11 +26,12 @@ import { MxCommunicationModule } from '../services/mx-communication'; import { MarketplaceEventsRepository } from 'src/db/marketplaces/marketplace-events.repository'; import { BlacklistedCollectionsRepository } from 'src/db/blacklistedCollections/blacklisted.repository'; import { ReportCollectionsRepository } from 'src/db/reports'; +import { AssetLikeEntity, AssetsLikesRepository } from 'src/db/assets'; @Global() @Module({ imports: [ - TypeOrmModule.forFeature([AssetsLikesRepository]), + TypeOrmModule.forFeature([AssetLikeEntity]), TypeOrmModule.forFeature([FeaturedCollectionsRepository]), TypeOrmModule.forFeature([FeaturedNftsRepository]), TypeOrmModule.forFeature([BlacklistedCollectionsRepository]), @@ -59,6 +59,7 @@ import { ReportCollectionsRepository } from 'src/db/reports'; CollectionStatsRepository, AuctionsRepository, OffersRepository, + AssetsLikesRepository ], exports: [PersistenceService, UsdPriceService], }) diff --git a/src/common/services/caching/caching.module.ts b/src/common/services/caching/caching.module.ts index 9f4905088..5f020a7de 100644 --- a/src/common/services/caching/caching.module.ts +++ b/src/common/services/caching/caching.module.ts @@ -8,6 +8,7 @@ import { } from '@multiversx/sdk-nestjs'; import { forwardRef, Global, Module } from '@nestjs/common'; import { CommonModule } from 'src/common.module'; +import { LocalRedisCacheService } from './local-redis-cache.service'; @Global() @Module({ @@ -35,14 +36,10 @@ import { CommonModule } from 'src/common.module'; }), ), ], - providers: [ - // LocalRedisCacheService, - LocalCacheService, - CachingModuleOptions, - ], + providers: [LocalRedisCacheService, LocalCacheService, CachingModuleOptions], exports: [ LocalCacheService, - // LocalRedisCacheService, + LocalRedisCacheService, ElrondCachingModule.forRoot( new RedisCacheModuleOptions({ host: process.env.REDIS_URL, diff --git a/src/common/services/caching/local-redis-cache.service.ts b/src/common/services/caching/local-redis-cache.service.ts index d09782bcf..43cbbe53e 100644 --- a/src/common/services/caching/local-redis-cache.service.ts +++ b/src/common/services/caching/local-redis-cache.service.ts @@ -1,47 +1,45 @@ -// import { RedisCacheService } from '@multiversx/sdk-nestjs'; -// import { Inject, Injectable, Logger } from '@nestjs/common'; -// import { isNil } from '@nestjs/common/utils/shared.utils'; -// import Redis from 'ioredis'; -// export const REDIS_CLIENT_TOKEN = 'REDIS_CLIENT_TOKEN'; -// @Injectable() -// export class LocalRedisCacheService { -// constructor( -// private readonly logger: Logger, -// private readonly redisCacheService: RedisCacheService, -// @Inject('REDIS_CLIENT_TOKEN') private readonly redis: Redis, -// ) {} +import { RedisCacheService } from '@multiversx/sdk-nestjs'; +import { Injectable, Logger } from '@nestjs/common'; +import { isNil } from '@nestjs/common/utils/shared.utils'; +export const REDIS_CLIENT_TOKEN = 'REDIS_CLIENT_TOKEN'; +@Injectable() +export class LocalRedisCacheService { + constructor( + private readonly logger: Logger, + private readonly redisCacheService: RedisCacheService, + ) {} -// async getOrSetWithDifferentTtl( -// key: string, -// createValueFunc: () => any, -// ): Promise { -// const cachedData = await this.redisCacheService.get(key); -// if (!isNil(cachedData)) { -// return cachedData; -// } -// const value = await this.buildInternalCreateValueFunc(key, createValueFunc); -// await this.redisCacheService.set(key, value, value.ttl); + async getOrSetWithDifferentTtl( + key: string, + createValueFunc: () => any, + ): Promise { + const cachedData = await this.redisCacheService.get(key); + if (!isNil(cachedData)) { + return cachedData; + } + const value = await this.buildInternalCreateValueFunc(key, createValueFunc); + await this.redisCacheService.set(key, value, value.ttl); -// return value; -// } + return value; + } -// private async buildInternalCreateValueFunc( -// key: string, -// createValueFunc: () => any, -// ): Promise { -// try { -// let data = createValueFunc(); -// if (data instanceof Promise) { -// data = await data; -// } -// return data; -// } catch (err) { -// this.logger.error(`An error occurred while trying to load value.`, { -// path: 'redis-cache.service.createValueFunc', -// exception: err, -// key, -// }); -// return null; -// } -// } -// } + private async buildInternalCreateValueFunc( + key: string, + createValueFunc: () => any, + ): Promise { + try { + let data = createValueFunc(); + if (data instanceof Promise) { + data = await data; + } + return data; + } catch (err) { + this.logger.error(`An error occurred while trying to load value.`, { + path: 'redis-cache.service.createValueFunc', + exception: err, + key, + }); + return null; + } + } +} diff --git a/src/db/assets/assets-likes.repository.ts b/src/db/assets/assets-likes.repository.ts index eeffe19cf..407da4893 100644 --- a/src/db/assets/assets-likes.repository.ts +++ b/src/db/assets/assets-likes.repository.ts @@ -1,15 +1,22 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; import { MYSQL_ALREADY_EXISTS } from 'src/utils/constants'; -import { DeleteResult, EntityRepository, Repository } from 'typeorm'; +import { DeleteResult, Repository } from 'typeorm'; import { AssetLikeEntity } from './assets-likes.entity'; -@EntityRepository(AssetLikeEntity) -export class AssetsLikesRepository extends Repository { +@Injectable() +export class AssetsLikesRepository { + + constructor( + @InjectRepository(AssetLikeEntity) + private assetsLikeRepository: Repository, + ) {} async getAssetsLiked( limit: number = 20, offset: number = 0, address: string, ): Promise<[AssetLikeEntity[], number]> { - const assetsLiked = await this.createQueryBuilder('assetsLiked') + const assetsLiked = await this.assetsLikeRepository.createQueryBuilder('assetsLiked') .where({ address: address, }) @@ -21,7 +28,7 @@ export class AssetsLikesRepository extends Repository { } async isAssetLiked(identifier: string, address: string): Promise { - const count = await this.count({ + const count = await this.assetsLikeRepository.count({ where: { identifier, address, @@ -32,7 +39,7 @@ export class AssetsLikesRepository extends Repository { } async getAssetLikesCount(identifier: string): Promise { - return await this.count({ + return await this.assetsLikeRepository.count({ where: { identifier, }, @@ -40,7 +47,7 @@ export class AssetsLikesRepository extends Repository { } async getLikesCountForAddress(address: string): Promise { - return await this.count({ + return await this.assetsLikeRepository.count({ where: { address, }, @@ -48,7 +55,7 @@ export class AssetsLikesRepository extends Repository { } async getBulkAssetLikesCount(identifiers: string[]): Promise { - return await this.createQueryBuilder('al') + return await this.assetsLikeRepository.createQueryBuilder('al') .select('al.identifier as identifier') .addSelect('COUNT(al.identifier) as likesCount') .where(`al.identifier IN(${identifiers.map((value) => `'${value}'`)})`, { @@ -59,7 +66,7 @@ export class AssetsLikesRepository extends Repository { } async getIsLikedAsset(identifiers: string[]): Promise { - return await this.createQueryBuilder('al') + return await this.assetsLikeRepository.createQueryBuilder('al') .select('CONCAT(al.identifier,"_",al.address) as identifier') .addSelect('true as liked') .where( @@ -77,7 +84,7 @@ export class AssetsLikesRepository extends Repository { async addLike(assetLikeEntity: AssetLikeEntity): Promise { try { - return await this.save(assetLikeEntity); + return await this.assetsLikeRepository.save(assetLikeEntity); } catch (err) { // If like already exists, we ignore the error. if (err.errno === MYSQL_ALREADY_EXISTS) { @@ -88,7 +95,7 @@ export class AssetsLikesRepository extends Repository { } async removeLike(identifier: string, address: string): Promise { - return await this.delete({ + return await this.assetsLikeRepository.delete({ identifier, address, }); @@ -98,7 +105,7 @@ export class AssetsLikesRepository extends Repository { offset?: number, limit?: number, ): Promise { - return await this.createQueryBuilder('al') + return await this.assetsLikeRepository.createQueryBuilder('al') .select('count(*) as cnt, al.identifier') .groupBy('al.identifier') .orderBy('cnt', 'DESC') diff --git a/src/modules/account-stats/tests/accounts-stats.service.spec.ts b/src/modules/account-stats/tests/accounts-stats.service.spec.ts index d83261eee..ef38d8848 100644 --- a/src/modules/account-stats/tests/accounts-stats.service.spec.ts +++ b/src/modules/account-stats/tests/accounts-stats.service.spec.ts @@ -6,9 +6,8 @@ import { } from 'nest-winston'; import * as winston from 'winston'; import * as Transport from 'winston-transport'; -import { MxApiService, RedisCacheService } from 'src/common'; +import { MxApiService } from 'src/common'; import { AccountStatsEntity } from 'src/db/account-stats/account-stats'; -import { RedisCacheServiceMock } from 'src/common/services/caching/redis-cache.service.mock'; import { MxApiServiceMock } from 'src/common/services/mx-communication/mx-api.service.mock'; import { AccountStatsRepository } from 'src/db/account-stats/account-stats.repository'; import { AccountStatsRepositoryMock } from 'src/db/account-stats/account-stats.repository-mock'; @@ -39,11 +38,6 @@ describe.skip('AccountsStatsService', () => { useClass: AccountStatsRepositoryMock, }; - const RedisCacheServiceProvider = { - provide: RedisCacheService, - useClass: RedisCacheServiceMock, - }; - const logTransports: Transport[] = [ new winston.transports.Console({ format: winston.format.combine( @@ -59,7 +53,6 @@ describe.skip('AccountsStatsService', () => { AccountStatsRepositoryProvider, AccountsStatsService, MarketplaceServiceProvider, - RedisCacheServiceProvider, AccountsStatsCachingServiceProvider, ], imports: [ diff --git a/src/modules/assets/asset-by-identifier.service.ts b/src/modules/assets/asset-by-identifier.service.ts index 85440c621..8934161cd 100644 --- a/src/modules/assets/asset-by-identifier.service.ts +++ b/src/modules/assets/asset-by-identifier.service.ts @@ -5,24 +5,23 @@ import { Asset, NftTypeEnum } from './models'; import { generateCacheKeyFromParams } from 'src/utils/generate-cache-key'; import { TimeConstants } from 'src/utils/time-utils'; import { Constants, RedisCacheService } from '@multiversx/sdk-nestjs'; +import { LocalRedisCacheService } from 'src/common/services/caching/local-redis-cache.service'; @Injectable() export class AssetByIdentifierService { constructor( private apiService: MxApiService, private readonly logger: Logger, - // private localRedisCacheService: LocalRedisCacheService, - private localRedisCacheService: RedisCacheService, + private localRedisCacheService: LocalRedisCacheService, // private localRedisCacheService: RedisCacheService, ) {} public async getAsset(identifier: string): Promise { try { const cacheKey = this.getAssetsCacheKey(identifier); const getAsset = () => this.getMappedAssetByIdentifier(identifier); - const asset = await this.localRedisCacheService.getOrSet( + const asset = await this.localRedisCacheService.getOrSetWithDifferentTtl( cacheKey, getAsset, - Constants.oneDay(), ); return asset?.value ? asset?.value : null; } catch (error) { diff --git a/src/modules/collection-stats/tests/collections-stats.service.spec.ts b/src/modules/collection-stats/tests/collections-stats.service.spec.ts index f56a95e7f..21b33e239 100644 --- a/src/modules/collection-stats/tests/collections-stats.service.spec.ts +++ b/src/modules/collection-stats/tests/collections-stats.service.spec.ts @@ -6,8 +6,7 @@ import { } from 'nest-winston'; import * as winston from 'winston'; import * as Transport from 'winston-transport'; -import { MxApiService, RedisCacheService } from 'src/common'; -import { RedisCacheServiceMock } from 'src/common/services/caching/redis-cache.service.mock'; +import { MxApiService } from 'src/common'; import { CollectionStatsRepository } from 'src/db/collection-stats/collection-stats.repository'; import { CollectionStatsRepositoryMock } from 'src/db/collection-stats/collection-stats.repository-mock'; import { CollectionStatsEntity } from 'src/db/collection-stats/collection-stats'; @@ -25,11 +24,6 @@ describe.skip('CollectionsStatsService', () => { useClass: CollectionStatsRepositoryMock, }; - const RedisCacheServiceProvider = { - provide: RedisCacheService, - useClass: RedisCacheServiceMock, - }; - const logTransports: Transport[] = [ new winston.transports.Console({ format: winston.format.combine( @@ -44,7 +38,6 @@ describe.skip('CollectionsStatsService', () => { MxApiServiceProvider, CollectionStatsRepositoryProvider, CollectionsStatsService, - RedisCacheServiceProvider, ], imports: [ WinstonModule.forRoot({ diff --git a/src/modules/common/redis-key-value-dataloader.handler.ts b/src/modules/common/redis-key-value-dataloader.handler.ts index b5a958e23..6dfac7680 100644 --- a/src/modules/common/redis-key-value-dataloader.handler.ts +++ b/src/modules/common/redis-key-value-dataloader.handler.ts @@ -47,10 +47,16 @@ export abstract class RedisKeyValueDataloaderHandler { const redisValues = this.mapValues(returnValues, data); for (const val of redisValues) { - const cacheKeys = this.getCacheKeys( - val.values.map((value) => value.key), - ); - await this.redisCacheService.setMany(cacheKeys, val.values, val.ttl); + if (val.values?.length > 0) { + const cacheKeys = this.getCacheKeys( + val.values.map((value) => value.key), + ); + await this.redisCacheService.setMany( + cacheKeys, + val.values, + val.ttl, + ); + } } return returnValues; } diff --git a/src/modules/offers/caching/offers-caching.service.ts b/src/modules/offers/caching/offers-caching.service.ts index 2c743df2d..aab4a67e7 100644 --- a/src/modules/offers/caching/offers-caching.service.ts +++ b/src/modules/offers/caching/offers-caching.service.ts @@ -1,35 +1,13 @@ import { Injectable } from '@nestjs/common'; import '../../../utils/extensions'; import { generateCacheKeyFromParams } from 'src/utils/generate-cache-key'; -import * as Redis from 'ioredis'; -import { cacheConfig } from 'src/config'; import { TimeConstants } from 'src/utils/time-utils'; -import * as hash from 'object-hash'; -import { Offer } from '../models'; import { OfferEntity } from 'src/db/offers'; -import { OffersFilters } from '../models/Offers-Filters'; -import { CachingService, RedisCacheService } from '@multiversx/sdk-nestjs'; +import { RedisCacheService } from '@multiversx/sdk-nestjs'; @Injectable() export class OffersCachingService { - private redisClient: Redis.Redis; - constructor( - private cacheService: CachingService, - private redisCacheService: RedisCacheService, - ) {} - - public async getOrSetOffers( - filters: OffersFilters, - offset: number = 0, - limit: number = 10, - getOffers: () => any, - ): Promise<[OfferEntity[], number]> { - return this.redisCacheService.getOrSet( - this.getOffersCacheKey(filters, offset, limit), - () => getOffers(), - 30 * TimeConstants.oneSecond, - ); - } + constructor(private redisCacheService: RedisCacheService) {} public async getOrSetOffersForAddress( address: string, @@ -63,20 +41,6 @@ export class OffersCachingService { await this.redisCacheService.delete( this.getOffersForCollectionCacheKey(collectionIdentifier), ); - await this.redisCacheService.deleteByPattern(this.getOffersCacheKey()); - } - - private getOffersCacheKey( - request?: OffersFilters, - offset: number = 0, - limit: number = 10, - ) { - return generateCacheKeyFromParams( - 'offersHash', - request ? hash(request) : '', - offset, - limit, - ); } private getOffersForOwnerCacheKey(address: string): string { diff --git a/src/modules/offers/offers.service.ts b/src/modules/offers/offers.service.ts index d3f1202e7..7ac10432d 100644 --- a/src/modules/offers/offers.service.ts +++ b/src/modules/offers/offers.service.ts @@ -27,24 +27,18 @@ export class OffersService { return await this.getOffersForCollection(filters, offset, limit); } - return await this.getOrSetOffers(filters, offset, limit); + return await this.getOffersFromDb(filters, offset, limit); } - private async getOrSetOffers( + private async getOffersFromDb( filters: OffersFilters, offset: number, limit: number, ): Promise<[Offer[], number]> { - let [offers, count] = await this.offersCachingService.getOrSetOffers( - filters, + let [offers, count] = await this.persistenceService.getOffers( + OffersFiltersForDb.formInputFilters(filters), offset, limit, - () => - this.persistenceService.getOffers( - OffersFiltersForDb.formInputFilters(filters), - offset, - limit, - ), ); return [offers.map((o) => Offer.fromEntity(o)), count]; } diff --git a/src/modules/search/tests/search.service.spec.ts b/src/modules/search/tests/search.service.spec.ts index df683be42..90540c9a3 100644 --- a/src/modules/search/tests/search.service.spec.ts +++ b/src/modules/search/tests/search.service.spec.ts @@ -5,8 +5,7 @@ import { } from 'nest-winston'; import * as winston from 'winston'; import * as Transport from 'winston-transport'; -import { MxApiService, MxIdentityService, RedisCacheService } from 'src/common'; -import { RedisCacheServiceMock } from 'src/common/services/caching/redis-cache.service.mock'; +import { MxApiService, MxIdentityService } from 'src/common'; import { MxApiServiceMock } from 'src/common/services/mx-communication/mx-api.service.mock'; import { SearchService } from '../search.service'; import { MxIdentityServiceMock } from 'src/common/services/mx-communication/mx-identity.service.mock'; @@ -28,11 +27,6 @@ describe.skip('SearchService', () => { useClass: MxIdentityServiceMock, }; - const RedisCacheServiceProvider = { - provide: RedisCacheService, - useClass: RedisCacheServiceMock, - }; - const logTransports: Transport[] = [ new winston.transports.Console({ format: winston.format.combine( @@ -47,7 +41,6 @@ describe.skip('SearchService', () => { MxApiServiceProvider, MxIdentityServiceProvider, SearchService, - RedisCacheServiceProvider, ], imports: [ WinstonModule.forRoot({ diff --git a/src/modules/tags/tests/tags.service.spec.ts b/src/modules/tags/tests/tags.service.spec.ts index b6ed3c703..4b92267b2 100644 --- a/src/modules/tags/tests/tags.service.spec.ts +++ b/src/modules/tags/tests/tags.service.spec.ts @@ -5,8 +5,7 @@ import { } from 'nest-winston'; import * as winston from 'winston'; import * as Transport from 'winston-transport'; -import { MxApiService, RedisCacheService } from 'src/common'; -import { RedisCacheServiceMock } from 'src/common/services/caching/redis-cache.service.mock'; +import { MxApiService } from 'src/common'; import { MxApiServiceMock } from 'src/common/services/mx-communication/mx-api.service.mock'; import { TagsService } from '../tags.service'; import { Tag } from '../models'; @@ -14,7 +13,6 @@ import { TagsFilter } from '../models/Tags.Filter'; import { TagsRepository } from 'src/db/auctions/tags.repository'; import { TagsRepositoryMock } from 'src/db/auctions/tags.repository.mock'; import { CachingService } from '@multiversx/sdk-nestjs'; -import { CachingServiceMock } from 'src/common/services/caching/caching.service.mock'; describe.skip('SearchService', () => { let service: TagsService; @@ -23,15 +21,6 @@ describe.skip('SearchService', () => { useClass: MxApiServiceMock, }; - const RedisCacheServiceProvider = { - provide: RedisCacheService, - useClass: RedisCacheServiceMock, - }; - const CachingServiceProvider = { - provide: CachingService, - useClass: CachingServiceMock, - }; - const TagsRepositoryProvider = { provide: TagsRepository, useClass: TagsRepositoryMock, @@ -47,13 +36,7 @@ describe.skip('SearchService', () => { ]; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ - providers: [ - MxApiServiceProvider, - CachingServiceProvider, - TagsService, - RedisCacheServiceProvider, - TagsRepositoryProvider, - ], + providers: [MxApiServiceProvider, TagsService, TagsRepositoryProvider], imports: [ WinstonModule.forRoot({ transports: logTransports,