@websolutespa/bom-mixer-store 1.2.2 → 1.3.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-mixer-store",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Mixer Store module of the BOM Repository",
5
5
  "keywords": [
6
6
  "bom",
@@ -1,10 +1,12 @@
1
1
  import {
2
- ICategorized, ICategory, IEntity, IEquatable, IMenu, IMenuCategory, IMenuGroup, IMenuItem,
2
+ ICategorized, ICategory, IEntity,
3
+ IMenu, IMenuCategory, IMenuGroup, IMenuItem,
3
4
  IMenuLink, IMenuPage, IMenuRoute, IRoute, IStore,
4
5
  IUndecoratedMenu, IUndecoratedMenuCategory, IUndecoratedMenuGroup, IUndecoratedMenuItem,
5
- IUndecoratedMenuLink, IUndecoratedMenuPage, Pagination, QueryParams, localizeItem
6
+ IUndecoratedMenuLink, IUndecoratedMenuPage,
7
+ QueryParams
6
8
  } from '@websolutespa/bom-core';
7
- import { MockService } from './mock.service';
9
+ import { MockService, localizeItem_, localizeItems_ } from './mock.service';
8
10
 
9
11
  export const MAX_INT = 1000000000000; // 9007199254740991
10
12
 
@@ -12,11 +14,15 @@ let UID = 0;
12
14
 
13
15
  export class MockMenuService<T extends IEntity = IEntity> extends MockService<T> {
14
16
 
17
+ /*
15
18
  async findOne<B extends T = T>(id: IEquatable, params: QueryParams = {}): Promise<B | undefined> {
16
19
  // console.log('MockMenuService.findOne', id, params);
17
20
  let item = await super.findOne<B>(id, params);
18
21
  if (item && params.market && params.locale) {
19
- item = await decorateMenu(this.store, (item as any), params.market, params.locale) as any;
22
+ item = await decorateMenu_(this.store, (item as any), {
23
+ market: params.market,
24
+ locale: params.locale,
25
+ }) as any;
20
26
  }
21
27
  return item;
22
28
  }
@@ -25,7 +31,10 @@ export class MockMenuService<T extends IEntity = IEntity> extends MockService<T>
25
31
  // console.log('MockMenuService.findMany', params);
26
32
  let items = await super.findMany<B>(params);
27
33
  if (params.market && params.locale) {
28
- items = await decorateMenus(this.store, (items as any[]), params.market, params.locale) as any[];
34
+ items = await decorateMenus_(this.store, (items as any[]), {
35
+ market: params.market,
36
+ locale: params.locale,
37
+ }) as any[];
29
38
  }
30
39
  return items;
31
40
  }
@@ -34,20 +43,40 @@ export class MockMenuService<T extends IEntity = IEntity> extends MockService<T>
34
43
  // console.log('MockMenuService.findPaged', params);
35
44
  const pagination = await super.findPaged<B>(params);
36
45
  if (params.market && params.locale) {
37
- pagination.docs = await decorateMenus(this.store, (pagination.docs as any[]), params.market, params.locale) as any[];
46
+ pagination.docs = await decorateMenus_(this.store, (pagination.docs as any[]), {
47
+ market: params.market,
48
+ locale: params.locale,
49
+ }) as any[];
38
50
  }
39
51
  return pagination;
40
52
  }
53
+ */
41
54
 
42
- protected override decorator_<B extends T = T>(item: B, params: QueryParams = {}): B {
43
- if (params.locale) {
44
- return localizeItem(item, params.locale);
45
- } else {
46
- return item;
55
+ protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {
56
+ let decoratedItem = await localizeItem_(item, params);
57
+ // decoratedItem = await decorateHref_<B>(this.store, decoratedItem, params);
58
+ if (params.market && params.locale) {
59
+ decoratedItem = await decorateMenu_(this.store, decoratedItem as any as IUndecoratedMenu, {
60
+ market: params.market,
61
+ locale: params.locale,
62
+ }) as any;
47
63
  }
64
+ return decoratedItem;
65
+ }
66
+
67
+ protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {
68
+ let decoratedItems = await localizeItems_(items, params);
69
+ // decoratedItems = await decorateHrefs_<B>(this.store, decoratedItems, params);
70
+ if (params.market && params.locale) {
71
+ decoratedItems = await decorateMenus_(this.store, decoratedItems as any as IUndecoratedMenu[], {
72
+ market: params.market,
73
+ locale: params.locale,
74
+ }) as any[];
75
+ }
76
+ return decoratedItems;
48
77
  }
49
- }
50
78
 
79
+ }
51
80
 
52
81
  async function collectPages(store: IStore, items?: IUndecoratedMenuItem[], params: QueryParams = {}) {
53
82
  if (!items) {
@@ -65,6 +94,8 @@ async function collectPages(store: IStore, items?: IUndecoratedMenuItem[], param
65
94
  if (value) {
66
95
  const routes = await store.route.findMany<IRoute>({
67
96
  where: {
97
+ ...(params.market ? { market: { equals: params.market } } : {}),
98
+ ...(params.locale ? { locale: { equals: params.locale } } : {}),
68
99
  page: {
69
100
  equals: item.page.value,
70
101
  },
@@ -72,8 +103,6 @@ async function collectPages(store: IStore, items?: IUndecoratedMenuItem[], param
72
103
  equals: item.page.relationTo,
73
104
  },
74
105
  },
75
- market: params.market,
76
- locale: params.locale,
77
106
  });
78
107
  const page = {
79
108
  ...value,
@@ -92,31 +121,34 @@ async function collectPages(store: IStore, items?: IUndecoratedMenuItem[], param
92
121
  }
93
122
  }
94
123
 
95
- export async function decorateMenu(store: IStore, item: IUndecoratedMenu, market: string = 'ww', locale: string = 'en'): Promise<IMenu> {
124
+ export async function decorateMenu_(store: IStore, item: IUndecoratedMenu, params: { market: string, locale: string }): Promise<IMenu> {
96
125
  const items = item.items || [];
97
126
  const decoratedItem: IMenu = {
98
127
  id: item.id,
99
128
  items: [],
100
129
  };
101
- const categories = await store.category.findMany<ICategory>({ market, locale });
130
+ const categories = await store.category.findMany<ICategory>(params);
102
131
  const routes = await store.route.findMany<IRoute>({
132
+ ...params,
103
133
  where: {
104
- market: {
105
- equals: market,
106
- },
107
- locale: {
108
- equals: locale,
109
- },
134
+ ...(params.market ? { market: { equals: params.market } } : {}),
135
+ ...(params.locale ? { locale: { equals: params.locale } } : {}),
110
136
  },
111
- market, locale,
112
137
  });
113
- await collectPages(store, items, { market, locale });
114
- decoratedItem.items = items.filter(x => hasMarket(x, market)).map(x => decorateMenuItem(x, categories, routes, market));
138
+ await collectPages(store, items, params);
139
+ decoratedItem.items = items
140
+ .filter(x => hasMarket(x, params.market))
141
+ .map(x => decorateMenuItem(x, categories, routes, params.market));
115
142
  return decoratedItem;
116
143
  }
117
144
 
118
- export async function decorateMenus(store: IStore, items: IUndecoratedMenu[], market: string = 'ww', locale: string = 'en'): Promise<IMenu[]> {
119
- return await Promise.all(items.map(x => decorateMenu(store, x, market, locale)));
145
+ export async function decorateMenus_(store: IStore, items: IUndecoratedMenu[], params: { market: string, locale: string }): Promise<IMenu[]> {
146
+ const decoratedItems: IMenu[] = [];
147
+ for (const item of items) {
148
+ const decoratedItem = await decorateMenu_(store, item, params);
149
+ decoratedItems.push(decoratedItem);
150
+ }
151
+ return decoratedItems;
120
152
  }
121
153
 
122
154
  export function decorateMenuCategory(
@@ -251,7 +283,10 @@ export function decorateMenuPage(
251
283
  market: string
252
284
  ): IMenuItem {
253
285
  const items = item.items || [];
254
- const route = routes.find(x => x.schema === item.page.relationTo && x.page === item.page.value.id);
286
+ const route = routes.find(x =>
287
+ x.schema === item.page.relationTo &&
288
+ x.page === item.page.value.id
289
+ );
255
290
  const decoratedItem: IMenuPage = {
256
291
  id: item.id || '',
257
292
  type: item.type,
@@ -1,75 +1,154 @@
1
- import { IEntity, IEquatable, IRoute, ISchema, IStore, Pagination, QueryParams, localizeItem } from '@websolutespa/bom-core';
2
- import { MockService } from './mock.service';
1
+ import { IDecoratedNav, IEntity, IEquatable, IMenuPage, IRoute, ISchema, IStore, IUndecoratedNav, QueryParams, isArray, isObject, isUndecoratedNav } from '@websolutespa/bom-core';
2
+ import { MockService, decorateRelation_, decorateRelations_, localizeItem_, localizeItems_ } from './mock.service';
3
3
 
4
4
  export class MockPageService<T extends IEntity = IEntity> extends MockService<T> {
5
5
 
6
- async findOne<B extends T = T>(id: IEquatable, params: QueryParams = {}): Promise<B | undefined> {
7
- // console.log('MockPageService.findOne', id, params);
8
- let item = await super.findOne<B>(id, params);
9
- if (item && params.market && params.locale) {
10
- item = await decorateHref<B>(this.store, item, params.market, params.locale);
11
- }
6
+ protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {
7
+ let decoratedItem = await localizeItem_(item, params);
8
+ decoratedItem = await decorateHref_<B>(this.store, decoratedItem, params);
9
+ decoratedItem = await decorateNav_<B>(this.store, decoratedItem, params);
10
+ decoratedItem = await decorateRelation_<B>(this.store, decoratedItem, {
11
+ market: params.market,
12
+ locale: params.locale,
13
+ depth: params.depth,
14
+ });
15
+ return decoratedItem;
16
+ }
17
+
18
+ protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {
19
+ let decoratedItems = await localizeItems_(items, params);
20
+ decoratedItems = await decorateHrefs_<B>(this.store, decoratedItems, params);
21
+ decoratedItems = await decorateNavs_<B>(this.store, decoratedItems, params);
22
+ decoratedItems = await decorateRelations_<B>(this.store, decoratedItems, {
23
+ market: params.market,
24
+ locale: params.locale,
25
+ depth: params.depth,
26
+ });
27
+ return decoratedItems;
28
+ }
29
+
30
+ }
31
+
32
+ export async function decorateHref_<T extends ISchema = ISchema>(store: IStore, item: T, params: QueryParams = {}): Promise<T | T & { href: string | null }> {
33
+ if (params.market && params.locale) {
34
+ const routes = await store.route.findMany<IRoute>({
35
+ where: {
36
+ market: {
37
+ equals: params.market,
38
+ },
39
+ locale: {
40
+ equals: params.locale,
41
+ },
42
+ schema: {
43
+ equals: item.schema,
44
+ },
45
+ page: {
46
+ equals: item.id,
47
+ },
48
+ },
49
+ });
50
+ const href = routes.length ? routes[0].id : null;
51
+ return { ...item, href };
52
+ } else {
12
53
  return item;
13
54
  }
55
+ }
14
56
 
15
- async findMany<B extends T = T>(params: QueryParams = {}): Promise<B[]> {
16
- // console.log('MockPageService.findMany', params);
17
- let items = await super.findMany<B>(params);
18
- if (params.market && params.locale) {
19
- items = await decorateHrefs<B>(this.store, items, params.market, params.locale);
20
- }
57
+ export async function decorateHrefs_<T extends ISchema = ISchema>(store: IStore, items: T[], params: QueryParams = {}): Promise<T[] | (T & { href: string | null })[]> {
58
+ if (params.market && params.locale) {
59
+ const routes = await store.route.findMany<IRoute>({
60
+ where: {
61
+ market: {
62
+ equals: params.market,
63
+ },
64
+ locale: {
65
+ equals: params.locale,
66
+ },
67
+ /*
68
+ ...(params.market ? { market: { equals: params.market } } : {}),
69
+ ...(params.locale ? { locale: { equals: params.locale } } : {}),
70
+ */
71
+ },
72
+ });
73
+ return items.map(item => {
74
+ const route = routes.find(x =>
75
+ x.schema === item.schema &&
76
+ x.page === item.id
77
+ );
78
+ const href = route ? route.id : null;
79
+ return { ...item, href };
80
+ });
81
+ } else {
21
82
  return items;
22
83
  }
84
+ }
23
85
 
24
- async findPaged<B extends T = T>(params: QueryParams = {}): Promise<{ docs: B[]; } & Pagination> {
25
- // console.log('MockPageService.findPaged', params);
26
- const pagination = await super.findPaged<B>(params);
27
- if (params.market && params.locale) {
28
- pagination.docs = await decorateHrefs<B>(this.store, pagination.docs, params.market, params.locale);
29
- }
30
- return pagination;
86
+ export async function decorateNav_<T extends ISchema = ISchema>(store: IStore, item: T, params: QueryParams = {}, routes?: IRoute[]): Promise<T> {
87
+ if (!routes) {
88
+ routes = await store.route.findMany<IRoute>({
89
+ where: {
90
+ ...(params.market ? { market: { equals: params.market } } : {}),
91
+ ...(params.locale ? { locale: { equals: params.locale } } : {}),
92
+ },
93
+ });
31
94
  }
32
-
33
- protected override decorator_<B extends T = T>(item: B, params: QueryParams = {}): B {
34
- if (params.locale) {
35
- return localizeItem(item, params.locale);
36
- } else {
37
- return item;
38
- }
95
+ const navs = collectNavs_(item);
96
+ for (const nav of navs) {
97
+ const decoratedNav = decorateNav__(nav, routes);
98
+ (nav as IDecoratedNav<T>).nav = decoratedNav.nav;
39
99
  }
100
+ return item;
40
101
  }
41
102
 
42
- export async function decorateHref<T extends ISchema = ISchema>(store: IStore, item: T, market: string = 'ww', locale: string = 'en'): Promise<T & { href: string | null }> {
103
+ export async function decorateNavs_<T extends ISchema = ISchema>(store: IStore, items: T[], params: QueryParams = {}): Promise<T[]> {
43
104
  const routes = await store.route.findMany<IRoute>({
105
+ ...params,
44
106
  where: {
45
- schema: {
46
- equals: item.schema
47
- },
48
- page: {
49
- equals: item.id
50
- },
51
- market: {
52
- equals: market
53
- },
54
- locale: {
55
- equals: locale
56
- },
57
- }
107
+ ...(params.market ? { market: { equals: params.market } } : {}),
108
+ ...(params.locale ? { locale: { equals: params.locale } } : {}),
109
+ },
58
110
  });
59
- const href = routes.length ? routes[0].id : null;
60
- return { ...item, href };
111
+ for (const item of items) {
112
+ await decorateNav_<T>(store, item, params, routes);
113
+ }
114
+ return items;
61
115
  }
62
116
 
63
- export async function decorateHrefs<T extends ISchema = ISchema>(store: IStore, items: T[], market: string = 'ww', locale: string = 'en'): Promise<(T & { href: string | null })[]> {
64
- const routes = await store.route.findMany<IRoute>();
65
- return items.map(item => {
117
+ // collecting navs recursively
118
+ function collectNavs_<T>(item: unknown, navs: IUndecoratedNav<T>[] = []): IUndecoratedNav<T>[] {
119
+ if (isUndecoratedNav<T>(item)) {
120
+ navs.push(item);
121
+ }
122
+ // console.log(item, isUndecoratedNav<T>(item));
123
+ if (isArray(item)) {
124
+ item.forEach(x => collectNavs_(x, navs));
125
+ } else if (isObject(item)) {
126
+ Object.values(item).forEach(value => {
127
+ collectNavs_(value, navs);
128
+ });
129
+ }
130
+ return navs;
131
+ }
132
+
133
+ function decorateNav__<T>(item: IUndecoratedNav<T>, routes: IRoute[]): IDecoratedNav<T> {
134
+ const { nav, ...undecorated } = item;
135
+ if (nav !== undefined) {
136
+ const decorated = { ...undecorated } as { nav?: IMenuPage };
66
137
  const route = routes.find(x =>
67
- x.schema === item.schema &&
68
- x.page === item.id &&
69
- x.market === market &&
70
- x.locale === locale
138
+ x.page === (nav.page.value as unknown as IEquatable) &&
139
+ x.schema === nav.page.relationTo
71
140
  );
72
- const href = route ? route.id : null;
73
- return { ...item, href };
74
- });
141
+ if (route) {
142
+ decorated.nav = {
143
+ type: 'page',
144
+ id: route.id,
145
+ href: route.id,
146
+ title: route.title,
147
+ page: (nav.page.value as unknown as IEquatable),
148
+ schema: nav.page.relationTo,
149
+ };
150
+ }
151
+ return decorated as IDecoratedNav<T>;
152
+ }
153
+ return undecorated as IDecoratedNav<T>;
75
154
  }
@@ -1,4 +1,6 @@
1
- import { IEntity, IPageRelation, IStore, JsonService, localizeItem, QueryParams } from '@websolutespa/bom-core';
1
+ import { cloneItem, IEntity, IPageUnresolvedRelation, isArray, ISchema, isObject, IStore, isUnresolvedRelation, JsonService, localizeItem, QueryParams } from '@websolutespa/bom-core';
2
+
3
+ export const MOCK_SERVICE_DEFAULT_DEPTH = 2;
2
4
 
3
5
  export class MockService<T extends IEntity = IEntity> extends JsonService<T> {
4
6
  store: IStore;
@@ -8,66 +10,139 @@ export class MockService<T extends IEntity = IEntity> extends JsonService<T> {
8
10
  this.store = store;
9
11
  }
10
12
 
11
- // !!! todo handle relations
13
+ protected override async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {
14
+ let decoratedItem = await localizeItem_(item, params);
15
+ decoratedItem = await decorateRelation_<B>(this.store, decoratedItem, {
16
+ market: params.market,
17
+ locale: params.locale,
18
+ depth: params.depth != null ? params.depth : MOCK_SERVICE_DEFAULT_DEPTH,
19
+ });
20
+ return decoratedItem;
21
+ }
12
22
 
13
- /*
14
- override async findOne<B extends T = T>(id: IEquatable, params: QueryParams = {}): Promise<B | undefined> {
15
- console.log('MockService.findOne', id, params);
16
- let item = await super.findOne<B>(id, params);
17
- // relations
18
- if (item) {
19
- if (params.depth !== 0) {
20
- item = await this.relatedItems_(item);
21
- }
22
- }
23
- return item;
23
+ protected override async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {
24
+ let decoratedItems = await localizeItems_(items, params);
25
+ decoratedItems = await decorateRelations_<B>(this.store, decoratedItems, {
26
+ market: params.market,
27
+ locale: params.locale,
28
+ depth: params.depth != null ? params.depth : MOCK_SERVICE_DEFAULT_DEPTH,
29
+ });
30
+ return decoratedItems;
24
31
  }
25
32
 
26
- override async findMany<B extends T = T>(params: QueryParams = {}): Promise<B[]> {
27
- // console.log('MockService.findMany', params);
28
- let items = await super.findMany<B>(params);
29
- // relations
30
- if (params.depth !== 0) {
31
- items = await Promise.all(items.map(x => this.relatedItems_(x)));
32
- }
33
- return items;
33
+ }
34
+
35
+ export async function localizeItem_<B>(item: B, params: QueryParams = {}): Promise<B> {
36
+ if (params.locale) {
37
+ return localizeItem(item, params.locale);
38
+ } else {
39
+ return cloneItem(item);
34
40
  }
41
+ }
42
+
43
+ export async function localizeItems_<B>(items: B[], params: QueryParams = {}): Promise<B[]> {
44
+ const decoratedItems: B[] = [];
45
+ for (const item of items) {
46
+ const decoratedItem = params.locale ?
47
+ localizeItem(item, params.locale) :
48
+ cloneItem(item);
49
+ decoratedItems.push(decoratedItem);
50
+ }
51
+ return decoratedItems;
52
+ }
53
+
35
54
 
36
- override async findPaged<B extends T = T>(params: QueryParams = {}): Promise<{ docs: B[]; } & Pagination> {
37
- // console.log('MockService.findPaged', params);
38
- const pagination = await super.findPaged<B>(params);
39
- // relations
40
- if (params.depth !== 0) {
41
- pagination.docs = await Promise.all(pagination.docs.map(x => this.relatedItems_(x)));
55
+ // getting item relation !!!unused
56
+ function getRelations_(item: Record<string, any>): IPageUnresolvedRelation[] {
57
+ const relations: IPageUnresolvedRelation[] = [];
58
+ if (isObject(item)) {
59
+ if (item.relation && isUnresolvedRelation(item.relation)) {
60
+ relations.push(item.relation);
42
61
  }
43
- return pagination;
44
62
  }
63
+ return relations;
64
+ }
45
65
 
46
- protected async relatedItems_<B extends T = T>(item: B): Promise<B> {
47
- const x = { ...item };
48
- Object.entries(item).forEach(([k, v]) => {
49
- if (isRelation(v)) {
50
- console.log(k, v);
66
+ // collecting relations recursively
67
+ function collectRelations_(item: unknown, relations: IPageUnresolvedRelation[] = []): IPageUnresolvedRelation[] {
68
+ if (isObject(item)) {
69
+ Object.entries(item).forEach(([key, value]) => {
70
+ if (key === 'relation' && isUnresolvedRelation(value)) {
71
+ // skip circular reference
72
+ if (value.relationTo !== item.schema || value.value !== item.id) {
73
+ relations.push(value);
74
+ }
75
+ } else if (isArray(value)) {
76
+ value.forEach(x => collectRelations_(x, relations));
77
+ } else if (isObject(value)) {
78
+ collectRelations_(value, relations);
51
79
  }
52
80
  });
53
- return x;
54
81
  }
55
- */
82
+ return relations;
83
+ }
56
84
 
57
- protected override decorator_<B extends T = T>(item: B, params: QueryParams = {}): B {
58
- if (params.locale) {
59
- return localizeItem(item, params.locale);
60
- } else {
61
- return item;
85
+ /*
86
+ const RELATION_CACHE = new Map<IEquatable, IEntity>();
87
+
88
+ export async function decorateRelation_<T extends ISchema = ISchema>(store: IStore, item: T, params: QueryParams = {}): Promise<T> {
89
+ const depth = params.depth != null ? params.depth : 0;
90
+ if (depth === 0) {
91
+ return item;
92
+ }
93
+ if (RELATION_CACHE.has(item.id)) {
94
+ return RELATION_CACHE.get(item.id) as T;
95
+ } else {
96
+ // item = await decorateRelation_<B>(this.store, item, params.market, params.locale);
97
+ const relations = collectRelations_(item);
98
+ for (const relation of relations) {
99
+ const isMany = Array.isArray(relation.value);
100
+ const items = await store[relation.relationTo].findMany({
101
+ ...params,
102
+ where: {
103
+ id: { in: isMany ? relation.value : [relation.value] },
104
+ },
105
+ depth: depth - 1,
106
+ });
107
+ if (isMany) {
108
+ relation.value = items as any; // !!!
109
+ } else {
110
+ relation.value = (items.length ? items[0] : undefined) as any; // !!!
111
+ }
62
112
  }
113
+ RELATION_CACHE.set(item.id, item);
63
114
  }
64
-
115
+ return item;
65
116
  }
117
+ */
66
118
 
67
- export function isObject(item: unknown): item is Record<string, unknown> {
68
- return item !== null && (typeof item === 'object');
119
+ export async function decorateRelation_<T extends ISchema = ISchema>(store: IStore, item: T, params: QueryParams = {}): Promise<T> {
120
+ const depth = params.depth != null ? params.depth : MOCK_SERVICE_DEFAULT_DEPTH;
121
+ if (depth === 0) {
122
+ return item;
123
+ }
124
+ const relations = collectRelations_(item);
125
+ for (const relation of relations) {
126
+ const isMany = Array.isArray(relation.value);
127
+ const items = await store[relation.relationTo].findMany({
128
+ ...params,
129
+ where: {
130
+ id: { in: isMany ? relation.value : [relation.value] },
131
+ },
132
+ depth: depth - 1,
133
+ });
134
+ if (isMany) {
135
+ relation.value = items as any; // !!!
136
+ } else {
137
+ relation.value = (items.length ? items[0] : undefined) as any; // !!!
138
+ }
139
+ }
140
+ return item;
69
141
  }
70
142
 
71
- export function isRelation(item: unknown): item is IPageRelation {
72
- return isObject(item) && typeof item.relationTo === 'string' && item.value !== undefined;
143
+ export async function decorateRelations_<T extends ISchema = ISchema>(store: IStore, items: T[], params: QueryParams = {}): Promise<T[]> {
144
+ for (const item of items) {
145
+ await decorateRelation_<T>(store, item, params);
146
+ }
147
+ return items;
73
148
  }
@@ -23,7 +23,7 @@ export class RemoteService<T extends IEntity = IEntity> implements IQuerable<T>
23
23
  // console.log('RemoteService', this.key, 'findOne', url);
24
24
  const item = await remoteApiGet<B>(url);
25
25
  if (item) {
26
- return this.decorator_(item, params);
26
+ return await this.decorateOne_(item, params);
27
27
  }
28
28
  return;
29
29
  }
@@ -37,7 +37,7 @@ export class RemoteService<T extends IEntity = IEntity> implements IQuerable<T>
37
37
  // console.log('RemoteService', this.key, 'findMany', url);
38
38
  const response = await remoteApiGet(url) as any; // !!! todo pagination<B[]> | B[]
39
39
  const items: B[] = Array.isArray(response.docs) ? response.docs : response;
40
- return items.map(x => this.decorator_(x, params));
40
+ return await this.decorateMany_(items, params);
41
41
  }
42
42
 
43
43
  async findPaged<B extends T = T>(params: QueryParams = {}): Promise<{ docs: B[]; } & Pagination> {
@@ -49,30 +49,33 @@ export class RemoteService<T extends IEntity = IEntity> implements IQuerable<T>
49
49
  // console.log('RemoteService', this.key, 'findPaged', url);
50
50
  const response = await remoteApiGet<{ docs: B[]; } & Pagination>(url);
51
51
  let items: B[] = response.docs;
52
- items = items.map(x => this.decorator_(x, params));
52
+ items = await this.decorateMany_(items, params);
53
53
  return { ...response, docs: items };
54
54
  }
55
55
 
56
+ // !!! todo params
56
57
  async create<B extends T = T>(payload: Omit<B, 'id'>): Promise<B> {
57
58
  const url = `/${this.key}`;
58
59
  // console.log('RemoteService', this.key, 'create', url);
59
60
  const item = await remoteApiPost<B>(url, payload);
60
- return this.decorator_(item);
61
+ return await this.decorateOne_(item, {});
61
62
  }
62
63
 
64
+ // !!! todo params
63
65
  async update<B extends T = T>(payload: Partial<B>): Promise<B> {
64
66
  const url = `/${this.key}`;
65
67
  // console.log('RemoteService', this.key, 'update', url);
66
68
  const item = await remoteApiPut<B>(url, payload);
67
- return this.decorator_(item);
69
+ return await this.decorateOne_(item, {});
68
70
  }
69
71
 
72
+ // !!! todo params
70
73
  async delete<B extends T = T>(id: IEquatable): Promise<B | undefined> {
71
74
  const params = {
72
75
  where: {
73
76
  id: {
74
77
  equals: id,
75
- }
78
+ },
76
79
  },
77
80
  };
78
81
  const query = qsSerialize(params);
@@ -80,12 +83,14 @@ export class RemoteService<T extends IEntity = IEntity> implements IQuerable<T>
80
83
  const url = `/${this.key}${search}`;
81
84
  // console.log('RemoteService', this.key, 'delete', url);
82
85
  const item = await remoteApiDelete<B>(url);
83
- return this.decorator_(item);
86
+ return await this.decorateOne_(item, {});
84
87
  }
85
88
 
86
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
87
- protected decorator_<B extends T = T>(item: B, params: QueryParams = {}): B {
89
+ protected async decorateOne_<B extends T = T>(item: B, params: QueryParams = {}): Promise<B> {
88
90
  return item;
89
91
  }
90
92
 
93
+ protected async decorateMany_<B extends T = T>(items: B[], params: QueryParams = {}): Promise<B[]> {
94
+ return items;
95
+ }
91
96
  }