@infrab4a/connect-angular 5.8.16 → 5.8.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/eslint.config.js +18 -0
  2. package/index.ts +1 -0
  3. package/karma.conf.js +32 -0
  4. package/ng-package.json +8 -0
  5. package/package.json +4 -24
  6. package/project.json +37 -0
  7. package/src/angular-connect.module.ts +266 -0
  8. package/src/angular-elastic-search.module.ts +23 -0
  9. package/src/angular-firebase-auth.module.ts +101 -0
  10. package/src/angular-firestore.module.ts +443 -0
  11. package/src/angular-hasura-graphql.module.ts +269 -0
  12. package/src/angular-vertex-search.module.ts +23 -0
  13. package/src/consts/backend-url.const.ts +1 -0
  14. package/src/consts/category-structure.ts +1 -0
  15. package/src/consts/default-shop.const.ts +1 -0
  16. package/src/consts/es-config.const.ts +1 -0
  17. package/src/consts/firebase-const.ts +5 -0
  18. package/src/consts/hasura-options.const.ts +1 -0
  19. package/src/consts/index.ts +8 -0
  20. package/src/consts/persistence.const.ts +1 -0
  21. package/src/consts/storage-base-url.const.ts +1 -0
  22. package/src/consts/vertex-config.const.ts +1 -0
  23. package/src/helpers/index.ts +1 -0
  24. package/src/helpers/mobile-operation-system-checker.helper.ts +10 -0
  25. package/src/index.ts +6 -0
  26. package/src/interfaces/catalog-strategies.interface.ts +36 -0
  27. package/src/interfaces/category-facades.interface.ts +7 -0
  28. package/src/interfaces/index.ts +2 -0
  29. package/src/persistence/cookie-data-persistence.ts +21 -0
  30. package/src/persistence/data-persistence.ts +7 -0
  31. package/src/persistence/index.ts +2 -0
  32. package/src/services/auth.service.ts +39 -0
  33. package/src/services/cart/cart-services.facade.ts +14 -0
  34. package/src/services/cart/index.ts +1 -0
  35. package/src/services/cart.service.ts +148 -0
  36. package/src/services/catalog/adapters/category-structure.adapter.ts +5 -0
  37. package/src/services/catalog/adapters/index.ts +3 -0
  38. package/src/services/catalog/adapters/new-category-structure.adapter.spec.ts +61 -0
  39. package/src/services/catalog/adapters/new-category-structure.adapter.ts +46 -0
  40. package/src/services/catalog/adapters/old-category-structure.adapter.ts +17 -0
  41. package/src/services/catalog/catalog.service.ts +153 -0
  42. package/src/services/catalog/category.service.ts +56 -0
  43. package/src/services/catalog/context/catalog-search.context.ts +59 -0
  44. package/src/services/catalog/enums/index.ts +1 -0
  45. package/src/services/catalog/enums/product-sorts.enum.ts +9 -0
  46. package/src/services/catalog/facades/catalog-service.facade.ts +32 -0
  47. package/src/services/catalog/facades/catalog-strategies.facade.ts +12 -0
  48. package/src/services/catalog/facades/category-repository.facade.ts +10 -0
  49. package/src/services/catalog/facades/category-service.facade.ts +25 -0
  50. package/{services/catalog/facades/index.d.ts → src/services/catalog/facades/index.ts} +5 -5
  51. package/src/services/catalog/facades/product-catalog.facade.ts +13 -0
  52. package/src/services/catalog/helpers/brand-manager.helper.ts +69 -0
  53. package/src/services/catalog/helpers/catalog-filter.helper.ts +50 -0
  54. package/src/services/catalog/helpers/catalog-sort.helper.ts +54 -0
  55. package/src/services/catalog/helpers/index.ts +4 -0
  56. package/src/services/catalog/helpers/product-fields.helper.ts +39 -0
  57. package/src/services/catalog/index.ts +7 -0
  58. package/src/services/catalog/models/category-with-tree.model.ts +7 -0
  59. package/src/services/catalog/models/index.ts +1 -0
  60. package/src/services/catalog/services/catalog-helpers.service.ts +35 -0
  61. package/src/services/catalog/services/catalog-operations.facade.ts +24 -0
  62. package/src/services/catalog/services/catalog-repository.service.ts +20 -0
  63. package/{services/catalog/services/index.d.ts → src/services/catalog/services/index.ts} +4 -4
  64. package/src/services/catalog/services/product-management.facade.ts +20 -0
  65. package/src/services/catalog/strategies/category-search.strategy.spec.ts +93 -0
  66. package/src/services/catalog/strategies/category-search.strategy.ts +181 -0
  67. package/src/services/catalog/strategies/index.ts +3 -0
  68. package/src/services/catalog/strategies/profile-search.strategy.ts +51 -0
  69. package/src/services/catalog/strategies/term-search.strategy.ts +163 -0
  70. package/src/services/catalog/strategies/types/strategy-params.type.ts +59 -0
  71. package/src/services/catalog/types/fetch-products-options.type.ts +10 -0
  72. package/src/services/catalog/types/fetch-products-params.type.ts +27 -0
  73. package/src/services/catalog/types/fetch-products-response.type.ts +11 -0
  74. package/src/services/catalog/types/index.ts +5 -0
  75. package/src/services/catalog/types/method-params.type.ts +21 -0
  76. package/src/services/catalog/types/product-sort.type.ts +3 -0
  77. package/src/services/catalog/wishlist.service.ts +364 -0
  78. package/src/services/checkout/checkout-dependencies.facade.ts +13 -0
  79. package/src/services/checkout/checkout-repositories.facade.ts +10 -0
  80. package/{services/checkout/index.d.ts → src/services/checkout/index.ts} +2 -2
  81. package/src/services/checkout.service.ts +236 -0
  82. package/src/services/coupon/coupon-repositories.facade.ts +11 -0
  83. package/{services/coupon/index.d.ts → src/services/coupon/index.ts} +1 -1
  84. package/src/services/coupon/types/coupon-params.type.ts +15 -0
  85. package/src/services/errors/group-invalid-coupon.error.ts +9 -0
  86. package/src/services/errors/index.ts +2 -0
  87. package/src/services/errors/invalid-coupon.error.ts +7 -0
  88. package/src/services/helpers/index.ts +1 -0
  89. package/src/services/helpers/util.helper.ts +17 -0
  90. package/src/services/home-shop/home-shop-repositories.facade.ts +11 -0
  91. package/{services/home-shop/index.d.ts → src/services/home-shop/index.ts} +1 -1
  92. package/src/services/home-shop.service.ts +210 -0
  93. package/src/services/index.ts +9 -0
  94. package/src/services/order-product-review.service.ts +135 -0
  95. package/src/services/order-recurrence.service.ts +121 -0
  96. package/src/services/order.service.ts +23 -0
  97. package/src/services/shared/configuration.facade.ts +21 -0
  98. package/src/services/shared/index.ts +1 -0
  99. package/src/services/types/index.ts +4 -0
  100. package/src/services/types/pending-product-review.type.ts +12 -0
  101. package/src/services/types/product-review-create.type.ts +15 -0
  102. package/src/services/types/required-checkout-data.type.ts +3 -0
  103. package/src/services/types/required-checkout-subscription-data.type.ts +3 -0
  104. package/src/test.ts +2 -0
  105. package/src/types/firebase-app-config.type.ts +1 -0
  106. package/src/types/index.ts +1 -0
  107. package/tsconfig.json +17 -0
  108. package/tsconfig.lib.json +13 -0
  109. package/tsconfig.lib.prod.json +13 -0
  110. package/tsconfig.spec.json +17 -0
  111. package/angular-connect.module.d.ts +0 -36
  112. package/angular-elastic-search.module.d.ts +0 -9
  113. package/angular-firebase-auth.module.d.ts +0 -11
  114. package/angular-firestore.module.d.ts +0 -17
  115. package/angular-hasura-graphql.module.d.ts +0 -14
  116. package/angular-vertex-search.module.d.ts +0 -9
  117. package/consts/backend-url.const.d.ts +0 -1
  118. package/consts/category-structure.d.ts +0 -1
  119. package/consts/default-shop.const.d.ts +0 -1
  120. package/consts/es-config.const.d.ts +0 -1
  121. package/consts/firebase-const.d.ts +0 -4
  122. package/consts/hasura-options.const.d.ts +0 -1
  123. package/consts/index.d.ts +0 -8
  124. package/consts/persistence.const.d.ts +0 -1
  125. package/consts/storage-base-url.const.d.ts +0 -1
  126. package/consts/vertex-config.const.d.ts +0 -1
  127. package/esm2020/angular-connect.module.mjs +0 -289
  128. package/esm2020/angular-elastic-search.module.mjs +0 -34
  129. package/esm2020/angular-firebase-auth.module.mjs +0 -141
  130. package/esm2020/angular-firestore.module.mjs +0 -625
  131. package/esm2020/angular-hasura-graphql.module.mjs +0 -421
  132. package/esm2020/angular-vertex-search.module.mjs +0 -34
  133. package/esm2020/consts/backend-url.const.mjs +0 -2
  134. package/esm2020/consts/category-structure.mjs +0 -2
  135. package/esm2020/consts/default-shop.const.mjs +0 -2
  136. package/esm2020/consts/es-config.const.mjs +0 -2
  137. package/esm2020/consts/firebase-const.mjs +0 -5
  138. package/esm2020/consts/hasura-options.const.mjs +0 -2
  139. package/esm2020/consts/index.mjs +0 -9
  140. package/esm2020/consts/persistence.const.mjs +0 -2
  141. package/esm2020/consts/storage-base-url.const.mjs +0 -2
  142. package/esm2020/consts/vertex-config.const.mjs +0 -2
  143. package/esm2020/helpers/index.mjs +0 -2
  144. package/esm2020/helpers/mobile-operation-system-checker.helper.mjs +0 -7
  145. package/esm2020/index.mjs +0 -7
  146. package/esm2020/infrab4a-connect-angular.mjs +0 -5
  147. package/esm2020/interfaces/catalog-strategies.interface.mjs +0 -2
  148. package/esm2020/interfaces/category-facades.interface.mjs +0 -2
  149. package/esm2020/interfaces/index.mjs +0 -3
  150. package/esm2020/persistence/cookie-data-persistence.mjs +0 -22
  151. package/esm2020/persistence/data-persistence.mjs +0 -2
  152. package/esm2020/persistence/index.mjs +0 -3
  153. package/esm2020/services/auth.service.mjs +0 -37
  154. package/esm2020/services/cart/cart-services.facade.mjs +0 -21
  155. package/esm2020/services/cart/index.mjs +0 -2
  156. package/esm2020/services/cart.service.mjs +0 -84
  157. package/esm2020/services/catalog/adapters/category-structure.adapter.mjs +0 -2
  158. package/esm2020/services/catalog/adapters/index.mjs +0 -4
  159. package/esm2020/services/catalog/adapters/new-category-structure.adapter.mjs +0 -43
  160. package/esm2020/services/catalog/adapters/old-category-structure.adapter.mjs +0 -23
  161. package/esm2020/services/catalog/catalog.service.mjs +0 -111
  162. package/esm2020/services/catalog/category.service.mjs +0 -42
  163. package/esm2020/services/catalog/context/catalog-search.context.mjs +0 -40
  164. package/esm2020/services/catalog/enums/index.mjs +0 -2
  165. package/esm2020/services/catalog/enums/product-sorts.enum.mjs +0 -11
  166. package/esm2020/services/catalog/facades/catalog-service.facade.mjs +0 -32
  167. package/esm2020/services/catalog/facades/catalog-strategies.facade.mjs +0 -17
  168. package/esm2020/services/catalog/facades/category-repository.facade.mjs +0 -20
  169. package/esm2020/services/catalog/facades/category-service.facade.mjs +0 -30
  170. package/esm2020/services/catalog/facades/index.mjs +0 -6
  171. package/esm2020/services/catalog/facades/product-catalog.facade.mjs +0 -21
  172. package/esm2020/services/catalog/helpers/brand-manager.helper.mjs +0 -62
  173. package/esm2020/services/catalog/helpers/catalog-filter.helper.mjs +0 -44
  174. package/esm2020/services/catalog/helpers/catalog-sort.helper.mjs +0 -52
  175. package/esm2020/services/catalog/helpers/index.mjs +0 -5
  176. package/esm2020/services/catalog/helpers/product-fields.helper.mjs +0 -43
  177. package/esm2020/services/catalog/index.mjs +0 -8
  178. package/esm2020/services/catalog/models/category-with-tree.model.mjs +0 -10
  179. package/esm2020/services/catalog/models/index.mjs +0 -2
  180. package/esm2020/services/catalog/services/catalog-helpers.service.mjs +0 -33
  181. package/esm2020/services/catalog/services/catalog-operations.facade.mjs +0 -26
  182. package/esm2020/services/catalog/services/catalog-repository.service.mjs +0 -25
  183. package/esm2020/services/catalog/services/index.mjs +0 -5
  184. package/esm2020/services/catalog/services/product-management.facade.mjs +0 -25
  185. package/esm2020/services/catalog/strategies/category-search.strategy.mjs +0 -133
  186. package/esm2020/services/catalog/strategies/index.mjs +0 -4
  187. package/esm2020/services/catalog/strategies/profile-search.strategy.mjs +0 -42
  188. package/esm2020/services/catalog/strategies/term-search.strategy.mjs +0 -126
  189. package/esm2020/services/catalog/strategies/types/strategy-params.type.mjs +0 -2
  190. package/esm2020/services/catalog/types/fetch-products-options.type.mjs +0 -2
  191. package/esm2020/services/catalog/types/fetch-products-params.type.mjs +0 -2
  192. package/esm2020/services/catalog/types/fetch-products-response.type.mjs +0 -2
  193. package/esm2020/services/catalog/types/index.mjs +0 -6
  194. package/esm2020/services/catalog/types/method-params.type.mjs +0 -2
  195. package/esm2020/services/catalog/types/product-sort.type.mjs +0 -2
  196. package/esm2020/services/catalog/wishlist.service.mjs +0 -258
  197. package/esm2020/services/checkout/checkout-dependencies.facade.mjs +0 -20
  198. package/esm2020/services/checkout/checkout-repositories.facade.mjs +0 -20
  199. package/esm2020/services/checkout/index.mjs +0 -3
  200. package/esm2020/services/checkout.service.mjs +0 -131
  201. package/esm2020/services/coupon/coupon-repositories.facade.mjs +0 -24
  202. package/esm2020/services/coupon/index.mjs +0 -2
  203. package/esm2020/services/helpers/index.mjs +0 -2
  204. package/esm2020/services/helpers/util.helper.mjs +0 -18
  205. package/esm2020/services/home-shop/home-shop-repositories.facade.mjs +0 -24
  206. package/esm2020/services/home-shop/index.mjs +0 -2
  207. package/esm2020/services/home-shop.service.mjs +0 -130
  208. package/esm2020/services/index.mjs +0 -10
  209. package/esm2020/services/order-product-review.service.mjs +0 -126
  210. package/esm2020/services/order-recurrence.service.mjs +0 -76
  211. package/esm2020/services/order.service.mjs +0 -30
  212. package/esm2020/services/shared/configuration.facade.mjs +0 -29
  213. package/esm2020/services/shared/index.mjs +0 -2
  214. package/esm2020/services/types/index.mjs +0 -5
  215. package/esm2020/services/types/pending-product-review.type.mjs +0 -2
  216. package/esm2020/services/types/product-review-create.type.mjs +0 -2
  217. package/esm2020/services/types/required-checkout-data.type.mjs +0 -2
  218. package/esm2020/services/types/required-checkout-subscription-data.type.mjs +0 -2
  219. package/esm2020/types/firebase-app-config.type.mjs +0 -2
  220. package/esm2020/types/index.mjs +0 -2
  221. package/fesm2015/infrab4a-connect-angular.mjs +0 -3476
  222. package/fesm2015/infrab4a-connect-angular.mjs.map +0 -1
  223. package/fesm2020/infrab4a-connect-angular.mjs +0 -3411
  224. package/fesm2020/infrab4a-connect-angular.mjs.map +0 -1
  225. package/helpers/index.d.ts +0 -1
  226. package/helpers/mobile-operation-system-checker.helper.d.ts +0 -3
  227. package/index.d.ts +0 -6
  228. package/interfaces/catalog-strategies.interface.d.ts +0 -31
  229. package/interfaces/category-facades.interface.d.ts +0 -6
  230. package/interfaces/index.d.ts +0 -2
  231. package/persistence/cookie-data-persistence.d.ts +0 -10
  232. package/persistence/data-persistence.d.ts +0 -6
  233. package/persistence/index.d.ts +0 -2
  234. package/services/auth.service.d.ts +0 -18
  235. package/services/cart/cart-services.facade.d.ts +0 -12
  236. package/services/cart/index.d.ts +0 -1
  237. package/services/cart.service.d.ts +0 -27
  238. package/services/catalog/adapters/category-structure.adapter.d.ts +0 -4
  239. package/services/catalog/adapters/index.d.ts +0 -3
  240. package/services/catalog/adapters/new-category-structure.adapter.d.ts +0 -12
  241. package/services/catalog/adapters/old-category-structure.adapter.d.ts +0 -10
  242. package/services/catalog/catalog.service.d.ts +0 -24
  243. package/services/catalog/category.service.d.ts +0 -23
  244. package/services/catalog/context/catalog-search.context.d.ts +0 -14
  245. package/services/catalog/enums/index.d.ts +0 -1
  246. package/services/catalog/enums/product-sorts.enum.d.ts +0 -9
  247. package/services/catalog/facades/catalog-service.facade.d.ts +0 -15
  248. package/services/catalog/facades/catalog-strategies.facade.d.ts +0 -10
  249. package/services/catalog/facades/category-repository.facade.d.ts +0 -9
  250. package/services/catalog/facades/category-service.facade.d.ts +0 -15
  251. package/services/catalog/facades/product-catalog.facade.d.ts +0 -10
  252. package/services/catalog/helpers/brand-manager.helper.d.ts +0 -14
  253. package/services/catalog/helpers/catalog-filter.helper.d.ts +0 -8
  254. package/services/catalog/helpers/catalog-sort.helper.d.ts +0 -7
  255. package/services/catalog/helpers/index.d.ts +0 -4
  256. package/services/catalog/helpers/product-fields.helper.d.ts +0 -7
  257. package/services/catalog/index.d.ts +0 -7
  258. package/services/catalog/models/category-with-tree.model.d.ts +0 -4
  259. package/services/catalog/models/index.d.ts +0 -1
  260. package/services/catalog/services/catalog-helpers.service.d.ts +0 -17
  261. package/services/catalog/services/catalog-operations.facade.d.ts +0 -13
  262. package/services/catalog/services/catalog-repository.service.d.ts +0 -12
  263. package/services/catalog/services/product-management.facade.d.ts +0 -12
  264. package/services/catalog/strategies/category-search.strategy.d.ts +0 -19
  265. package/services/catalog/strategies/index.d.ts +0 -3
  266. package/services/catalog/strategies/profile-search.strategy.d.ts +0 -14
  267. package/services/catalog/strategies/term-search.strategy.d.ts +0 -20
  268. package/services/catalog/strategies/types/strategy-params.type.d.ts +0 -59
  269. package/services/catalog/types/fetch-products-options.type.d.ts +0 -8
  270. package/services/catalog/types/fetch-products-params.type.d.ts +0 -25
  271. package/services/catalog/types/fetch-products-response.type.d.ts +0 -19
  272. package/services/catalog/types/index.d.ts +0 -5
  273. package/services/catalog/types/method-params.type.d.ts +0 -17
  274. package/services/catalog/types/product-sort.type.d.ts +0 -2
  275. package/services/catalog/wishlist.service.d.ts +0 -60
  276. package/services/checkout/checkout-dependencies.facade.d.ts +0 -10
  277. package/services/checkout/checkout-repositories.facade.d.ts +0 -9
  278. package/services/checkout.service.d.ts +0 -33
  279. package/services/coupon/coupon-repositories.facade.d.ts +0 -10
  280. package/services/helpers/index.d.ts +0 -1
  281. package/services/helpers/util.helper.d.ts +0 -3
  282. package/services/home-shop/home-shop-repositories.facade.d.ts +0 -10
  283. package/services/home-shop.service.d.ts +0 -34
  284. package/services/index.d.ts +0 -9
  285. package/services/order-product-review.service.d.ts +0 -18
  286. package/services/order-recurrence.service.d.ts +0 -25
  287. package/services/order.service.d.ts +0 -13
  288. package/services/shared/configuration.facade.d.ts +0 -12
  289. package/services/shared/index.d.ts +0 -1
  290. package/services/types/index.d.ts +0 -4
  291. package/services/types/pending-product-review.type.d.ts +0 -12
  292. package/services/types/product-review-create.type.d.ts +0 -14
  293. package/services/types/required-checkout-data.type.d.ts +0 -2
  294. package/services/types/required-checkout-subscription-data.type.d.ts +0 -2
  295. package/types/firebase-app-config.type.d.ts +0 -1
  296. package/types/index.d.ts +0 -1
@@ -0,0 +1,93 @@
1
+ import { Category, Product, RepositoryFindResult, Shops, Where } from '@infrab4a/connect'
2
+
3
+ import { CatalogFilterHelper } from '../helpers/catalog-filter.helper'
4
+ import { CatalogSortHelper } from '../helpers/catalog-sort.helper'
5
+ import { ProductFieldsHelper } from '../helpers/product-fields.helper'
6
+ import { CatalogHelpersService, CatalogRepositoryService } from '../services'
7
+
8
+ import { CategorySearchStrategy } from './category-search.strategy'
9
+
10
+ describe('CategorySearchStrategy', () => {
11
+ let findCatalog: jasmine.Spy
12
+ let buildProductFilterByCategory: jasmine.Spy
13
+ let strategy: CategorySearchStrategy
14
+ const limits = { limit: 24, offset: 0 }
15
+ const shop = Shops.GLAMSHOP
16
+ const emptyResult: RepositoryFindResult<Product> = {
17
+ data: [],
18
+ count: 0,
19
+ }
20
+
21
+ beforeEach(() => {
22
+ findCatalog = jasmine.createSpy('findCatalog').and.resolveTo(emptyResult)
23
+ buildProductFilterByCategory = jasmine
24
+ .createSpy('buildProductFilterByCategory')
25
+ .and.resolveTo({ id: { operator: Where.IN, value: ['1', '2'] } })
26
+
27
+ const repositoryService = {
28
+ getProductCatalog: () => ({
29
+ productCatalogRepository: { findCatalog },
30
+ categoryStructureAdapter: { buildProductFilterByCategory },
31
+ }),
32
+ } as unknown as CatalogRepositoryService
33
+
34
+ const helpersService = {
35
+ getFieldsHelper: () => new ProductFieldsHelper(),
36
+ getFilterHelper: () => new CatalogFilterHelper(),
37
+ getSortHelper: () => new CatalogSortHelper(),
38
+ } as unknown as CatalogHelpersService
39
+
40
+ strategy = new CategorySearchStrategy(repositoryService, helpersService)
41
+ })
42
+
43
+ it('should paginate wishlist most-relevant as best-sellers in the catalog query', async () => {
44
+ const category = { id: '10', isWishlist: true, products: ['1', '2'] } as Category
45
+
46
+ await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
47
+
48
+ expect(findCatalog).toHaveBeenCalledTimes(1)
49
+
50
+ const [repoParams, mainGender] = findCatalog.calls.mostRecent().args
51
+
52
+ expect(repoParams.limits).toEqual(limits)
53
+ expect(repoParams.orderBy).toEqual({
54
+ shoppingCount: 'desc',
55
+ rate: 'desc',
56
+ stock: 'desc',
57
+ name: 'asc',
58
+ })
59
+ expect(repoParams.fields).not.toEqual(['id'])
60
+ expect(mainGender).toBeNull()
61
+ })
62
+
63
+ it('should keep most-relevant in-memory path when category is not a wishlist', async () => {
64
+ const category = {
65
+ id: '20',
66
+ isWishlist: false,
67
+ getMostRelevantByShop: () => ['1'],
68
+ } as unknown as Category
69
+
70
+ await strategy.search({ category, filters: {}, limits, sort: 'most-relevant' }, shop)
71
+
72
+ expect(findCatalog).toHaveBeenCalledTimes(2)
73
+ expect(findCatalog.calls.argsFor(0)[0].fields).toEqual(['id'])
74
+ expect(findCatalog.calls.argsFor(0)[0].limits).toBeUndefined()
75
+ expect(findCatalog.calls.argsFor(1)[0].orderBy).toEqual({
76
+ shoppingCount: 'desc',
77
+ rate: 'desc',
78
+ stock: 'desc',
79
+ name: 'asc',
80
+ })
81
+ expect(findCatalog.calls.argsFor(1)[0].limits).toBeUndefined()
82
+ })
83
+
84
+ it('should use the requested sort when wishlist sort is not most-relevant', async () => {
85
+ const category = { id: '10', isWishlist: true, products: ['1'] } as Category
86
+
87
+ await strategy.search({ category, filters: {}, limits, sort: 'news' }, shop)
88
+
89
+ expect(findCatalog).toHaveBeenCalledTimes(1)
90
+ expect(findCatalog.calls.mostRecent().args[0].orderBy).toEqual({ createdAt: 'desc' })
91
+ expect(findCatalog.calls.mostRecent().args[0].limits).toEqual(limits)
92
+ })
93
+ })
@@ -0,0 +1,181 @@
1
+ import { Injectable } from '@angular/core'
2
+ import { isEmpty, Product, RepositoryCacheOptions, RepositoryFindResult, Shops, Where } from '@infrab4a/connect'
3
+
4
+ import { CatalogSearchParams, CatalogSearchStrategy } from '../../../interfaces'
5
+ import { CatalogHelpersService, CatalogRepositoryService } from '../services'
6
+
7
+ import {
8
+ FindAndSortByMostRelevantParams,
9
+ SearchDefaultParams,
10
+ SearchWithMostRelevantParams,
11
+ } from './types/strategy-params.type'
12
+
13
+ @Injectable()
14
+ export class CategorySearchStrategy implements CatalogSearchStrategy {
15
+ constructor(
16
+ private readonly repositoryService: CatalogRepositoryService,
17
+ private readonly helpersService: CatalogHelpersService,
18
+ ) {}
19
+
20
+ async search(
21
+ params: CatalogSearchParams,
22
+ shop: Shops,
23
+ optionsCache?: { cache?: RepositoryCacheOptions },
24
+ ): Promise<RepositoryFindResult<Product>> {
25
+ const { category, filters, limits, sort, mainGender } = params
26
+
27
+ if (!category) {
28
+ throw new Error('Category is required for CategorySearchStrategy')
29
+ }
30
+
31
+ const fields = this.helpersService.getFieldsHelper().getStandardFields()
32
+
33
+ if (category.isWishlist) {
34
+ return this.searchDefault({
35
+ category,
36
+ filters,
37
+ limits,
38
+ shop,
39
+ mainGender,
40
+ sort: sort === 'most-relevant' ? 'best-sellers' : sort,
41
+ optionsCache,
42
+ fields,
43
+ })
44
+ }
45
+
46
+ if (sort === 'most-relevant') {
47
+ return this.searchWithMostRelevant({
48
+ category,
49
+ filters,
50
+ limits,
51
+ shop,
52
+ mainGender,
53
+ optionsCache,
54
+ fields,
55
+ })
56
+ }
57
+
58
+ return this.searchDefault({
59
+ category,
60
+ filters,
61
+ limits,
62
+ shop,
63
+ mainGender,
64
+ sort,
65
+ optionsCache,
66
+ fields,
67
+ })
68
+ }
69
+
70
+ private async searchWithMostRelevant(params: SearchWithMostRelevantParams) {
71
+ const { category, filters, shop, optionsCache } = params
72
+ const mostRelevant = category.isWishlist ? [] : category.getMostRelevantByShop(shop)
73
+ const productsIds = await this.getProductIds(category, filters, optionsCache)
74
+
75
+ return this.findAndSortByMostRelevant({
76
+ mostRelevants: mostRelevant,
77
+ productIds: productsIds,
78
+ filters: params.filters,
79
+ limits: params.limits,
80
+ shop: params.shop,
81
+ mainGender: params.mainGender,
82
+ optionsCache: params.optionsCache,
83
+ fields: params.fields,
84
+ })
85
+ }
86
+
87
+ private async searchDefault(params: SearchDefaultParams) {
88
+ const { category, filters, limits, sort, optionsCache, fields } = params
89
+ const productCatalog = this.repositoryService.getProductCatalog()
90
+ const filterHelper = this.helpersService.getFilterHelper()
91
+ const sortHelper = this.helpersService.getSortHelper()
92
+
93
+ const repoParams = {
94
+ fields,
95
+ filters: {
96
+ ...(await productCatalog.categoryStructureAdapter.buildProductFilterByCategory(category)),
97
+ ...filterHelper.buildFilterQuery(filters || {}),
98
+ },
99
+ ...(sort ? { orderBy: sortHelper.buildSortQuery(sort) } : {}),
100
+ limits,
101
+ options: {
102
+ minimal: ['price'],
103
+ maximum: ['price'],
104
+ distinct: ['brand'],
105
+ },
106
+ }
107
+
108
+ return productCatalog.productCatalogRepository.findCatalog(repoParams, null, optionsCache)
109
+ }
110
+
111
+ private async getProductIds(category: any, filters: any, optionsCache: any): Promise<string[]> {
112
+ const productCatalog = this.repositoryService.getProductCatalog()
113
+ const filterHelper = this.helpersService.getFilterHelper()
114
+
115
+ return productCatalog.productCatalogRepository
116
+ .findCatalog(
117
+ {
118
+ fields: ['id'],
119
+ filters: {
120
+ ...(await productCatalog.categoryStructureAdapter.buildProductFilterByCategory(category)),
121
+ ...filterHelper.buildFilterQuery(filters || {}),
122
+ },
123
+ },
124
+ undefined,
125
+ optionsCache,
126
+ )
127
+ .then((products) => products.data.map((product) => product.id))
128
+ }
129
+
130
+ private async findAndSortByMostRelevant(
131
+ params: FindAndSortByMostRelevantParams,
132
+ ): Promise<RepositoryFindResult<Product>> {
133
+ const { mostRelevants, productIds, filters, shop, mainGender, optionsCache, fields, limits } = params
134
+ const productCatalog = this.repositoryService.getProductCatalog()
135
+ const filterHelper = this.helpersService.getFilterHelper()
136
+ const sortHelper = this.helpersService.getSortHelper()
137
+
138
+ const mostRelevantProductsIds = [...new Set(mostRelevants.concat(productIds))]
139
+ const totalResult = await productCatalog.productCatalogRepository.findCatalog(
140
+ {
141
+ fields,
142
+ filters: {
143
+ id: { operator: Where.IN, value: mostRelevantProductsIds },
144
+ ...filterHelper.buildFilterQuery(filters || {}),
145
+ },
146
+ orderBy: sortHelper.buildSortQuery('best-sellers'),
147
+ options: {
148
+ minimal: ['price'],
149
+ maximum: ['price'],
150
+ ...(isEmpty(filters?.brands) ? { distinct: ['brand'] } : {}),
151
+ },
152
+ },
153
+ mainGender || (shop === Shops.MENSMARKET ? 'male' : 'female'),
154
+ optionsCache,
155
+ )
156
+
157
+ return this.sortProductsByRelevance(totalResult, mostRelevants, limits)
158
+ }
159
+
160
+ private sortProductsByRelevance(totalResult: any, mostRelevants: string[], limits: any) {
161
+ const mostRelevantWithoutStock = totalResult.data.filter(
162
+ (product: any) => mostRelevants.includes(product.id) && product.stock.quantity <= 0,
163
+ )
164
+ const firstProducts = totalResult.data
165
+ .filter((product: any) => mostRelevants.includes(product.id) && product.stock.quantity > 0)
166
+ .sort((a: any, b: any) => mostRelevants.indexOf(a.id) - mostRelevants.indexOf(b.id))
167
+ const lastProducts = totalResult.data
168
+ .filter((product: any) => !mostRelevants.includes(product.id))
169
+ .concat(mostRelevantWithoutStock)
170
+ const categoryMostRelevants = firstProducts.concat(lastProducts)
171
+ const resultFinal = categoryMostRelevants.slice(limits.offset, limits.offset + limits.limit)
172
+
173
+ return {
174
+ data: resultFinal,
175
+ count: totalResult.count,
176
+ maximum: totalResult.maximum,
177
+ minimal: totalResult.minimal,
178
+ distinct: totalResult.distinct,
179
+ }
180
+ }
181
+ }
@@ -0,0 +1,3 @@
1
+ export * from './category-search.strategy'
2
+ export * from './profile-search.strategy'
3
+ export * from './term-search.strategy'
@@ -0,0 +1,51 @@
1
+ import { Injectable } from '@angular/core'
2
+ import { Product, RepositoryCacheOptions, RepositoryFindResult, Shops, Where } from '@infrab4a/connect'
3
+
4
+ import { CatalogSearchParams, CatalogSearchStrategy } from '../../../interfaces'
5
+ import { CatalogHelpersService, CatalogRepositoryService } from '../services'
6
+
7
+ @Injectable()
8
+ export class ProfileSearchStrategy implements CatalogSearchStrategy {
9
+ constructor(
10
+ private readonly repositoryService: CatalogRepositoryService,
11
+ private readonly helpersService: CatalogHelpersService,
12
+ ) {}
13
+
14
+ async search(
15
+ params: CatalogSearchParams,
16
+ shop: Shops,
17
+ optionsCache?: { cache?: RepositoryCacheOptions },
18
+ ): Promise<RepositoryFindResult<Product>> {
19
+ const { profile, filters, limits, sort, mainGender } = params
20
+
21
+ if (!profile) {
22
+ throw new Error('Profile is required for ProfileSearchStrategy')
23
+ }
24
+
25
+ const productCatalog = this.repositoryService.getProductCatalog()
26
+ const filterHelper = this.helpersService.getFilterHelper()
27
+ const sortHelper = this.helpersService.getSortHelper()
28
+ const fieldsHelper = this.helpersService.getFieldsHelper()
29
+
30
+ const repoParams = {
31
+ fields: fieldsHelper.getStandardFields(),
32
+ filters: {
33
+ tagsProfile: { operator: Where.LIKE, value: profile },
34
+ ...filterHelper.buildFilterQuery(filters || {}),
35
+ },
36
+ ...(sort ? { orderBy: sortHelper.buildSortQuery(sort) } : {}),
37
+ limits,
38
+ options: {
39
+ minimal: ['price'],
40
+ maximum: ['price'],
41
+ distinct: ['brand'],
42
+ },
43
+ }
44
+
45
+ return productCatalog.productCatalogRepository.findCatalog(
46
+ repoParams,
47
+ mainGender || (shop === Shops.MENSMARKET ? 'male' : 'female'),
48
+ optionsCache,
49
+ )
50
+ }
51
+ }
@@ -0,0 +1,163 @@
1
+ import { Injectable } from '@angular/core'
2
+ import {
3
+ isEmpty,
4
+ Product,
5
+ ProductSearch,
6
+ RepositoryCacheOptions,
7
+ RepositoryFindResult,
8
+ Shops,
9
+ Where,
10
+ } from '@infrab4a/connect'
11
+
12
+ import { CatalogSearchParams, CatalogSearchStrategy } from '../../../interfaces'
13
+ import { CatalogHelpersService, CatalogRepositoryService } from '../services'
14
+
15
+ import {
16
+ SortByTermRelevanceParams,
17
+ TermSearchDefaultParams,
18
+ TermSearchMostRelevantParams,
19
+ } from './types/strategy-params.type'
20
+
21
+ @Injectable()
22
+ export class TermSearchStrategy implements CatalogSearchStrategy {
23
+ private productsByTerm: Record<string, string[]> = {}
24
+
25
+ constructor(
26
+ private readonly repositoryService: CatalogRepositoryService,
27
+ private readonly helpersService: CatalogHelpersService,
28
+ ) {}
29
+
30
+ async search(
31
+ params: CatalogSearchParams,
32
+ shop: Shops,
33
+ _optionsCache?: { cache?: RepositoryCacheOptions },
34
+ ): Promise<RepositoryFindResult<Product>> {
35
+ const { term, filters, limits, sort, mainGender } = params
36
+
37
+ if (!term) {
38
+ throw new Error('Term is required for TermSearchStrategy')
39
+ }
40
+
41
+ const productIds = await this.findCatalogIdsBySearch(term, shop)
42
+
43
+ if (sort === 'most-relevant') {
44
+ return this.searchWithMostRelevant({
45
+ productIds,
46
+ filters,
47
+ limits,
48
+ shop,
49
+ mainGender,
50
+ })
51
+ }
52
+
53
+ return this.searchDefault({
54
+ productIds,
55
+ filters,
56
+ limits,
57
+ shop,
58
+ mainGender,
59
+ sort,
60
+ fields: this.helpersService.getFieldsHelper().getStandardFields(),
61
+ })
62
+ }
63
+
64
+ private async searchWithMostRelevant(params: TermSearchMostRelevantParams): Promise<RepositoryFindResult<Product>> {
65
+ const { productIds, filters, shop, mainGender } = params
66
+ const productCatalog = this.repositoryService.getProductCatalog()
67
+ const filterHelper = this.helpersService.getFilterHelper()
68
+
69
+ const totalResult = await productCatalog.productCatalogRepository.findCatalog(
70
+ {
71
+ fields: ['id', 'stock', 'gender'],
72
+ filters: {
73
+ id: { operator: Where.IN, value: productIds },
74
+ published: { operator: Where.EQUALS, value: true },
75
+ ...filterHelper.buildFilterQuery(filters || {}),
76
+ },
77
+ options: {
78
+ minimal: ['price'],
79
+ maximum: ['price'],
80
+ ...(isEmpty(filters?.brands) ? { distinct: ['brand'] } : {}),
81
+ },
82
+ },
83
+ // mainGender || (shop === Shops.MENSMARKET ? 'male' : 'female'),
84
+ )
85
+
86
+ return this.sortByTermRelevance({
87
+ productIds,
88
+ totalResult,
89
+ limits: params.limits,
90
+ filters,
91
+ fieldsHelper: this.helpersService.getFieldsHelper(),
92
+ })
93
+ }
94
+
95
+ private async searchDefault(params: TermSearchDefaultParams): Promise<RepositoryFindResult<Product>> {
96
+ const { productIds, filters, limits, sort, shop, mainGender, fields } = params
97
+ const productCatalog = this.repositoryService.getProductCatalog()
98
+ const filterHelper = this.helpersService.getFilterHelper()
99
+ const sortHelper = this.helpersService.getSortHelper()
100
+
101
+ return productCatalog.productCatalogRepository.findCatalog(
102
+ {
103
+ fields,
104
+ filters: {
105
+ id: { operator: Where.IN, value: productIds },
106
+ published: { operator: Where.EQUALS, value: true },
107
+ ...filterHelper.buildFilterQuery(filters || {}),
108
+ },
109
+ limits,
110
+ ...(sort ? { orderBy: sortHelper.buildSortQuery(sort) } : {}),
111
+ options: {
112
+ minimal: ['price'],
113
+ maximum: ['price'],
114
+ distinct: ['brand'],
115
+ },
116
+ },
117
+ // mainGender || (shop === Shops.MENSMARKET ? 'male' : 'female'),
118
+ )
119
+ }
120
+
121
+ private sortByTermRelevance(params: SortByTermRelevanceParams) {
122
+ const { productIds, totalResult, limits, filters, fieldsHelper } = params
123
+ // const defaultGender = filters?.gender?.at(0) || 'male'
124
+ const stockData = totalResult.data.filter((product: any) => product.stock.quantity > 0)
125
+ const stockOut = totalResult.data.filter((product: any) => product.stock.quantity <= 0)
126
+ const productIdsStock = productIds.filter((product) => stockData.some((result: any) => result.id === product))
127
+ const productIdsStockOut = productIds.filter((product) => stockOut.some((result: any) => result.id === product))
128
+ const limitedProductId = productIdsStock
129
+ .concat(productIdsStockOut)
130
+ .slice(limits.offset, limits.offset + limits.limit)
131
+
132
+ return this.getDetailedProducts(limitedProductId, totalResult, fieldsHelper)
133
+ }
134
+
135
+ private async getDetailedProducts(orderedId: string[], totalResult: any, fieldsHelper: any) {
136
+ const productCatalog = this.repositoryService.getProductCatalog()
137
+ const fields = fieldsHelper.getStandardFields()
138
+ const productResult = await productCatalog.productCatalogRepository.findCatalog({
139
+ filters: {
140
+ id: { operator: Where.IN, value: orderedId },
141
+ },
142
+ fields,
143
+ })
144
+
145
+ return {
146
+ data: orderedId.map((id) => productResult.data.find((product) => product.id === id)).filter(Boolean),
147
+ count: totalResult.count,
148
+ maximum: totalResult.maximum,
149
+ minimal: totalResult.minimal,
150
+ distinct: totalResult.distinct,
151
+ }
152
+ }
153
+
154
+ private async findCatalogIdsBySearch(term: string, shop: Shops): Promise<string[]> {
155
+ if (this.productsByTerm[term]) return this.productsByTerm[term]
156
+
157
+ const productServices = this.repositoryService.getProductServices()
158
+
159
+ return (this.productsByTerm[term] = await productServices.productSearch
160
+ .search<ProductSearch>(term, 999, shop === Shops.GLAMSHOP ? 'female' : 'male')
161
+ .then((products) => [...new Set(products.map((product) => product.id))]))
162
+ }
163
+ }
@@ -0,0 +1,59 @@
1
+ import { RepositoryCacheOptions, Shops } from '@infrab4a/connect'
2
+
3
+ export type SearchWithMostRelevantParams = {
4
+ category: any
5
+ filters: any
6
+ limits: any
7
+ shop: Shops
8
+ mainGender: any
9
+ optionsCache: { cache?: RepositoryCacheOptions } | undefined
10
+ fields: any
11
+ }
12
+
13
+ export type SearchDefaultParams = {
14
+ category: any
15
+ filters: any
16
+ limits: any
17
+ shop: Shops
18
+ mainGender: any
19
+ sort: any
20
+ optionsCache: { cache?: RepositoryCacheOptions } | undefined
21
+ fields: any
22
+ }
23
+
24
+ export type FindAndSortByMostRelevantParams = {
25
+ mostRelevants: string[]
26
+ productIds: string[]
27
+ filters: any
28
+ limits: any
29
+ shop: Shops
30
+ mainGender: any
31
+ optionsCache: { cache?: RepositoryCacheOptions } | undefined
32
+ fields: any
33
+ }
34
+
35
+ export type TermSearchMostRelevantParams = {
36
+ productIds: string[]
37
+ filters: any
38
+ limits: any
39
+ mainGender: any
40
+ shop: Shops
41
+ }
42
+
43
+ export type TermSearchDefaultParams = {
44
+ productIds: string[]
45
+ filters: any
46
+ limits: any
47
+ mainGender: any
48
+ shop: Shops
49
+ fields: any
50
+ sort?: any
51
+ }
52
+
53
+ export type SortByTermRelevanceParams = {
54
+ productIds: string[]
55
+ totalResult: any
56
+ limits: any
57
+ filters: any
58
+ fieldsHelper: any
59
+ }
@@ -0,0 +1,10 @@
1
+ import { ProductGender } from '@infrab4a/connect'
2
+
3
+ import { ProductSort } from './product-sort.type'
4
+
5
+ export type FetchProductsOptions = {
6
+ page?: number
7
+ perPage?: number
8
+ sort?: ProductSort
9
+ mainGender?: ProductGender
10
+ }
@@ -0,0 +1,27 @@
1
+ import { Category } from '@infrab4a/connect'
2
+
3
+ import { CatalogFilters } from '../../../interfaces'
4
+
5
+ import { FetchProductsOptions } from './fetch-products-options.type'
6
+
7
+ export type FetchProductsMainParams<T extends Category> = {
8
+ category?: T
9
+ profile?: string[]
10
+ term?: string
11
+ }
12
+
13
+ export type FetchProductsParams<T extends Category> = FetchProductsOptions &
14
+ (
15
+ | {
16
+ category: FetchProductsMainParams<T>['category']
17
+ filters?: CatalogFilters & { customOptions?: never }
18
+ }
19
+ | {
20
+ profile: FetchProductsMainParams<T>['profile']
21
+ filters?: CatalogFilters & { customOptions?: never; tags?: never }
22
+ }
23
+ | {
24
+ term: FetchProductsMainParams<T>['term']
25
+ filters?: CatalogFilters & { customOptions?: never }
26
+ }
27
+ )
@@ -0,0 +1,11 @@
1
+ import { Product } from '@infrab4a/connect'
2
+
3
+ export type FetchProductsResponse = {
4
+ products: { data: Product[]; total: number }
5
+ pages: number
6
+ prices: {
7
+ price: { min: number; max: number }
8
+ subscriberPrice: { min: number; max: number }
9
+ }
10
+ brands?: string[]
11
+ }
@@ -0,0 +1,5 @@
1
+ export * from './fetch-products-options.type'
2
+ export * from './fetch-products-params.type'
3
+ export * from './fetch-products-response.type'
4
+ export * from './method-params.type'
5
+ export * from './product-sort.type'
@@ -0,0 +1,21 @@
1
+ import { Category, Product, Shops } from '@infrab4a/connect'
2
+
3
+ import { CatalogSearchParams } from '../../../interfaces'
4
+
5
+ import { FetchProductsParams } from './fetch-products-params.type'
6
+
7
+ export type StockNotificationParams = {
8
+ shop: Shops
9
+ productId: string
10
+ name: string
11
+ email: string
12
+ }
13
+
14
+ export type BuildResponseParams<T extends Category = Category> = {
15
+ data: Product[]
16
+ total: number
17
+ maximum: any
18
+ minimal: any
19
+ searchParams: CatalogSearchParams
20
+ options: FetchProductsParams<T>
21
+ }
@@ -0,0 +1,3 @@
1
+ import { ProductSorts } from '../enums'
2
+
3
+ export type ProductSort = `${ProductSorts}`