strapi-plugin-navigation 1.0.2 → 1.1.1

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.
@@ -5,20 +5,20 @@ const slugify = require('slugify');
5
5
  const pluralize = require('pluralize');
6
6
  const { sanitizeEntity } = require('strapi-utils');
7
7
  const {
8
- isArray,
9
8
  isEmpty,
10
9
  isObject,
11
10
  isNil,
12
- isNumber,
11
+ toNumber,
12
+ isNaN,
13
13
  find,
14
14
  first,
15
15
  get,
16
- kebabCase,
17
16
  last,
18
17
  upperFirst,
19
18
  isString,
19
+ map,
20
20
  } = require('lodash');
21
- const { KIND_TYPES } = require("./utils/constant");
21
+ const { KIND_TYPES } = require('./utils/constant');
22
22
  const utilsFunctions = require('./utils/functions');
23
23
  const { renderType } = require('../models/navigation');
24
24
  const { type: itemType, additionalFields: configAdditionalFields } = require('../models/navigationItem');
@@ -32,29 +32,34 @@ const { type: itemType, additionalFields: configAdditionalFields } = require('..
32
32
  const excludedContentTypes = ['strapi::'];
33
33
 
34
34
  const contentTypesNameFieldsDefaults = [
35
- "title",
36
- "subject",
37
- "name",
35
+ 'title',
36
+ 'subject',
37
+ 'name',
38
38
  ];
39
- const contentTypesNameFields = get(
39
+ const getContentTypesNameFields = () => get(
40
40
  strapi.config,
41
- "custom.plugins.navigation.contentTypesNameFields",
41
+ 'plugins.navigation.contentTypesNameFields',
42
42
  {},
43
43
  );
44
44
 
45
45
  module.exports = {
46
+ isMongoDB() {
47
+ const { pluginName, model } = utilsFunctions.extractMeta(strapi.plugins);
48
+ const orm = strapi.query(model.modelName, pluginName).model.orm;
49
+ return orm === 'mongoose';
50
+ },
46
51
  // Get plugin configuration
47
- config: async () => {
52
+ async config() {
48
53
  const { pluginName, service, audienceModel } = utilsFunctions.extractMeta(strapi.plugins);
49
- const additionalFields = get(strapi.config, 'custom.plugins.navigation.additionalFields', []);
54
+ const additionalFields = get(strapi.config, 'plugins.navigation.additionalFields', []);
50
55
  let extendedResult = {};
51
56
  const result = {
52
57
  contentTypes: await service.configContentTypes(),
53
58
  contentTypesNameFields: {
54
59
  default: contentTypesNameFieldsDefaults,
55
- ...(isObject(contentTypesNameFields) ? contentTypesNameFields : {}),
60
+ ...getContentTypesNameFields(),
56
61
  },
57
- allowedLevels: get(strapi.config, 'custom.plugins.navigation.allowedLevels'),
62
+ allowedLevels: get(strapi.config, 'plugins.navigation.allowedLevels'),
58
63
  additionalFields,
59
64
  };
60
65
 
@@ -77,22 +82,20 @@ module.exports = {
77
82
  };
78
83
  },
79
84
 
80
- configContentTypes: async () => {
85
+ async configContentTypes() {
81
86
  const eligibleContentTypes =
82
87
  await Promise.all(
83
- Object.keys(strapi.contentTypes)
84
- .map(
85
- async (key) => {
86
- if (find(excludedContentTypes, name => key.includes(name))) { // exclude internal content types
87
- return;
88
- }
89
-
90
- const item = strapi.contentTypes[key];
91
- const { associations = [], kind, options, uid } = item;
92
- const { draftAndPublish } = options;
93
- const hasDefinedNavigationRelation = associations.some(_ => _.model === 'navigationitem');
94
-
95
- if (hasDefinedNavigationRelation) {
88
+ strapi.config.get('plugins.navigation.relatedContentTypes', [])
89
+ .filter(contentType => !!strapi.contentTypes[contentType])
90
+ .map(
91
+ async (key) => {
92
+ if (find(excludedContentTypes, name => key.includes(name))) { // exclude internal content types
93
+ return;
94
+ }
95
+ const item = strapi.contentTypes[key];
96
+ const { kind, options, uid } = item;
97
+ const { draftAndPublish } = options;
98
+
96
99
  const isSingleType = kind === KIND_TYPES.SINGLE;
97
100
  const isSingleTypeWithPublishFlow = isSingleType && draftAndPublish;
98
101
 
@@ -103,36 +106,40 @@ module.exports = {
103
106
 
104
107
  if (isSingleType) {
105
108
  if (isSingleTypeWithPublishFlow) {
106
- const itemsCountOrBypass = isSingleTypeWithPublishFlow ?
109
+ const itemsCountOrBypass = isSingleTypeWithPublishFlow ?
107
110
  await strapi.query(uid).count({
108
- _publicationState: 'live'
109
- }) :
111
+ _publicationState: 'live',
112
+ }) :
110
113
  true;
111
114
  return returnType(itemsCountOrBypass !== 0);
112
115
  }
113
116
  const isAvailable = await strapi.query(uid).count();
114
117
  return isAvailable === 1 ? returnType(true) : undefined;
115
- }
118
+ }
116
119
  return returnType(true);
117
- }
118
- return undefined;
119
- }
120
- )
120
+ },
121
+ ),
121
122
  );
122
123
  return eligibleContentTypes
123
124
  .filter(key => key)
124
- .map(({ key, available}) => {
125
+ .map(({ key, available }) => {
125
126
  const item = strapi.contentTypes[key];
126
127
  const relatedField = (item.associations || []).find(_ => _.model === 'navigationitem');
127
- const { uid, options, info, collectionName, apiName, plugin, kind } = item;
128
+ const { uid, options, info, collectionName, modelName, apiName, plugin, kind } = item;
128
129
  const { name, description } = info;
129
130
  const { isManaged, hidden, templateName } = options;
131
+ const findRouteConfig = find(get(strapi.api, `[${modelName}].config.routes`, []),
132
+ route => route.handler.includes('.find'));
133
+ const findRoutePath = findRouteConfig && findRouteConfig.path.split('/')[1];
134
+ const apiPath = findRoutePath && (findRoutePath !== apiName) ? findRoutePath : apiName || modelName;
130
135
  const isSingle = kind === KIND_TYPES.SINGLE;
131
- const endpoint = isSingle ? apiName : pluralize(apiName);
132
- const relationName = utilsFunctions.singularize(apiName);
136
+ const endpoint = isSingle ? apiPath : pluralize(apiPath);
137
+ const relationName = utilsFunctions.singularize(modelName);
133
138
  const relationNameParts = last(uid.split('.')).split('-');
134
- const contentTypeName = relationNameParts.length > 1 ? relationNameParts.reduce((prev, curr) => `${prev}${upperFirst(curr)}`, '') : upperFirst(apiName);
135
- const labelSingular = name || (upperFirst(relationNameParts.length > 1 ? relationNameParts.join(' ') : relationName));
139
+ const contentTypeName = relationNameParts.length > 1 ? relationNameParts.reduce(
140
+ (prev, curr) => `${prev}${upperFirst(curr)}`, '') : upperFirst(modelName);
141
+ const labelSingular = name ||
142
+ (upperFirst(relationNameParts.length > 1 ? relationNameParts.join(' ') : relationName));
136
143
  return {
137
144
  uid,
138
145
  name: relationName,
@@ -154,7 +161,7 @@ module.exports = {
154
161
  },
155
162
 
156
163
  // Get all available navigations
157
- get: async () => {
164
+ async get() {
158
165
  const { pluginName, masterModel } = utilsFunctions.extractMeta(strapi.plugins);
159
166
  const entities = await strapi
160
167
  .query(masterModel.modelName, pluginName)
@@ -165,7 +172,7 @@ module.exports = {
165
172
  },
166
173
 
167
174
  // Get navigation by id with related
168
- getById: async (id) => {
175
+ async getById(id) {
169
176
  const { pluginName, masterModel, itemModel } = utilsFunctions.extractMeta(strapi.plugins);
170
177
  const entity = await strapi
171
178
  .query(masterModel.modelName, pluginName)
@@ -177,17 +184,17 @@ module.exports = {
177
184
  master: id,
178
185
  _limit: -1,
179
186
  _sort: 'order:asc',
180
- }, ['related', 'audience']);
181
-
187
+ });
188
+ const entities = await this.getRelatedItems(entityItems);
182
189
  return {
183
190
  ...sanitizeEntity(entity,
184
191
  { model: masterModel },
185
192
  ),
186
- items: utilsFunctions.buildNestedStructure(entityItems),
193
+ items: utilsFunctions.buildNestedStructure(entities),
187
194
  };
188
195
  },
189
196
 
190
- post: async (payload, auditLog) => {
197
+ async post(payload, auditLog) {
191
198
  const { pluginName, masterModel, service } = utilsFunctions.extractMeta(strapi.plugins);
192
199
  const { name, visible } = payload;
193
200
 
@@ -196,19 +203,20 @@ module.exports = {
196
203
  .create({
197
204
  name,
198
205
  slug: slugify(name).toLowerCase(),
199
- visible,
206
+ visible: !!visible,
200
207
  });
201
208
 
202
209
  return service
203
210
  .createBranch(payload.items, existingEntity, null)
204
211
  .then(() => service.getById(existingEntity.id))
205
212
  .then((newEntity) => {
206
- utilsFunctions.sendAuditLog(auditLog, 'onChangeNavigation', { actionType: 'CREATE', oldEntity: existingEntity, newEntity });
213
+ utilsFunctions.sendAuditLog(auditLog, 'onChangeNavigation',
214
+ { actionType: 'CREATE', oldEntity: existingEntity, newEntity });
207
215
  return newEntity;
208
216
  });
209
217
  },
210
218
 
211
- put: async (id, payload, auditLog) => {
219
+ async put(id, payload, auditLog) {
212
220
  const { pluginName, masterModel, service } = utilsFunctions.extractMeta(strapi.plugins);
213
221
  const { name, visible } = payload;
214
222
 
@@ -220,7 +228,7 @@ module.exports = {
220
228
  {
221
229
  name: entityNameHasChanged ? name : existingEntity.name,
222
230
  slug: entityNameHasChanged ? slugify(name).toLowerCase() : existingEntity.slug,
223
- visible,
231
+ visible: !!visible,
224
232
  },
225
233
  );
226
234
  }
@@ -232,23 +240,43 @@ module.exports = {
232
240
  service.getById(existingEntity.id)],
233
241
  ))
234
242
  .then(([actionType, newEntity]) => {
235
- utilsFunctions.sendAuditLog(auditLog, 'onChangeNavigation', { actionType, oldEntity: existingEntity, newEntity });
243
+ utilsFunctions.sendAuditLog(auditLog, 'onChangeNavigation',
244
+ { actionType, oldEntity: existingEntity, newEntity });
236
245
  return newEntity;
237
246
  });
238
247
  },
239
248
 
240
- render: async (idOrSlug, type = renderType.FLAT, menuOnly = false) => {
249
+ async render(idOrSlug, type = renderType.FLAT, menuOnly = false) {
241
250
  const { service } = utilsFunctions.extractMeta(
242
251
  strapi.plugins,
243
252
  );
244
- const findById = isNumber(idOrSlug) || isUuid(idOrSlug);
253
+ const findById = !isNaN(toNumber(idOrSlug)) || isUuid(idOrSlug);
245
254
  const criteria = findById ? { id: idOrSlug } : { slug: idOrSlug };
246
255
  const itemCriteria = menuOnly ? { menuAttached: true } : {};
247
256
 
248
257
  return service.renderType(type, criteria, itemCriteria);
249
258
  },
250
259
 
251
- renderType: async (type = renderType.FLAT, criteria = {}, itemCriteria = {}) => {
260
+ async renderChildren(
261
+ idOrSlug,
262
+ childUIKey,
263
+ type = renderType.FLAT,
264
+ menuOnly = false,
265
+ ) {
266
+ const { service } = utilsFunctions.extractMeta(strapi.plugins);
267
+ const findById = !isNaN(toNumber(idOrSlug)) || isUuid(idOrSlug);
268
+ const criteria = findById ? { id: idOrSlug } : { slug: idOrSlug };
269
+ const filter = type === renderType.FLAT ? null : childUIKey;
270
+
271
+ const itemCriteria = {
272
+ ...(menuOnly && { menuAttached: true }),
273
+ ...(type === renderType.FLAT ? { uiRouterKey: childUIKey } : {}),
274
+ };
275
+
276
+ return service.renderType(type, criteria, itemCriteria, filter);
277
+ },
278
+
279
+ async renderType(type = renderType.FLAT, criteria = {}, itemCriteria = {}, filter = null) {
252
280
  const { pluginName, service, masterModel, itemModel } = utilsFunctions.extractMeta(
253
281
  strapi.plugins,
254
282
  );
@@ -260,31 +288,36 @@ module.exports = {
260
288
  visible: true,
261
289
  });
262
290
  if (entity && entity.id) {
263
- const items = await strapi.query(itemModel.modelName, pluginName).find(
291
+ const entities = await strapi.query(itemModel.modelName, pluginName).find(
264
292
  {
265
293
  master: entity.id,
266
294
  ...itemCriteria,
267
295
  _limit: -1,
268
296
  _sort: 'order:asc',
269
297
  },
270
- ["related", "audience"],
298
+ ['related', 'audience'],
271
299
  );
272
300
 
273
- if (!items) {
301
+ if (!entities) {
274
302
  return [];
275
303
  }
304
+ const items = await this.getRelatedItems(entities);
276
305
  const { contentTypes, contentTypesNameFields } = await service.config();
277
- const getTemplateName = await utilsFunctions.templateNameFactory(items, strapi, contentTypes)
278
306
 
279
- switch (type) {
307
+ switch (type?.toLowerCase()) {
280
308
  case renderType.TREE:
281
309
  case renderType.RFR:
310
+ const getTemplateName = await utilsFunctions.templateNameFactory(items, strapi, contentTypes);
282
311
  const itemParser = (item, path = '', field) => {
283
312
  const isExternal = item.type === itemType.EXTERNAL;
284
- const parentPath = isExternal ? undefined : `${path === '/' ? '' : path}/${item.path === '/' ? '' : item.path}`;
285
- const slug = isString(parentPath) ? slugify((first(parentPath) === '/' ? parentPath.substring(1) : parentPath).replace(/\//g, '-')) : undefined;
313
+ const parentPath = isExternal ? undefined : `${path === '/' ? '' : path}/${item.path === '/'
314
+ ? ''
315
+ : item.path}`;
316
+ const slug = isString(parentPath) ? slugify(
317
+ (first(parentPath) === '/' ? parentPath.substring(1) : parentPath).replace(/\//g, '-')) : undefined;
286
318
  const lastRelated = item.related ? last(item.related) : undefined;
287
319
  return {
320
+ id: item.id,
288
321
  title: utilsFunctions.composeItemTitle(item, contentTypesNameFields, contentTypes),
289
322
  menuAttached: item.menuAttached,
290
323
  path: isExternal ? item.externalPath : parentPath,
@@ -294,7 +327,7 @@ module.exports = {
294
327
  external: isExternal,
295
328
  related: isExternal || !lastRelated ? undefined : {
296
329
  ...lastRelated,
297
- __templateName: getTemplateName(lastRelated.__contentType, lastRelated.id),
330
+ __templateName: getTemplateName(lastRelated.relatedType, lastRelated.id),
298
331
  },
299
332
  audience: !isEmpty(item.audience) ? item.audience.map(aItem => aItem.key) : undefined,
300
333
  items: isExternal ? undefined : service.renderTree({
@@ -311,20 +344,26 @@ module.exports = {
311
344
  field: 'parent',
312
345
  itemParser,
313
346
  });
347
+
348
+ const filteredStructure = filter
349
+ ? treeStructure.filter((item) => item.uiRouterKey === filter)
350
+ : treeStructure;
351
+
314
352
  if (type === renderType.RFR) {
315
353
  return service.renderRFR({
316
- items: treeStructure,
354
+ items: filteredStructure,
317
355
  contentTypes,
318
356
  });
319
357
  }
320
- return treeStructure;
358
+ return filteredStructure;
321
359
  default:
322
360
  return items
323
361
  .filter(utilsFunctions.filterOutUnpublished)
324
362
  .map((item) => ({
325
363
  ...sanitizeEntity(item, { model: itemModel }),
364
+ audience: item.audience?.map(_ => _.key),
326
365
  title: utilsFunctions.composeItemTitle(item, contentTypesNameFields, contentTypes),
327
- related: item.related,
366
+ related: item.related?.map(({ localizations, ...item }) => item),
328
367
  items: null,
329
368
  }));
330
369
  }
@@ -332,13 +371,13 @@ module.exports = {
332
371
  throw strapi.errors.notFound();
333
372
  },
334
373
 
335
- renderTree: ({
374
+ renderTree({
336
375
  items = [],
337
376
  id = null,
338
377
  field = 'parent',
339
378
  path = '',
340
379
  itemParser = (i) => i,
341
- }) => {
380
+ }) {
342
381
  return items
343
382
  .filter(
344
383
  (item) => {
@@ -354,11 +393,11 @@ module.exports = {
354
393
  )
355
394
  .filter(utilsFunctions.filterOutUnpublished)
356
395
  .map(item => itemParser({
357
- ...item
396
+ ...item,
358
397
  }, path, field));
359
398
  },
360
399
 
361
- renderRFR: ({ items, parent = null, parentNavItem = null, contentTypes = [] }) => {
400
+ renderRFR({ items, parent = null, parentNavItem = null, contentTypes = [] }) {
362
401
  const { service } = utilsFunctions.extractMeta(strapi.plugins);
363
402
  let pages = {};
364
403
  let nav = {};
@@ -368,7 +407,7 @@ module.exports = {
368
407
  const { items: itemChilds, ...itemProps } = item;
369
408
  const itemNav = service.renderRFRNav(itemProps);
370
409
  const itemPage = service.renderRFRPage({
371
- item: itemProps,
410
+ item: itemProps,
372
411
  parent,
373
412
  });
374
413
 
@@ -402,15 +441,15 @@ module.exports = {
402
441
 
403
442
  if (!isEmpty(itemChilds)) {
404
443
  const { nav: nestedNavs } = service.renderRFR({
405
- items: itemChilds,
406
- parent: itemPage.id,
444
+ items: itemChilds,
445
+ parent: itemPage.id,
407
446
  parentNavItem: itemNav,
408
447
  contentTypes,
409
448
  });
410
449
  const { pages: nestedPages } = service.renderRFR({
411
- items: itemChilds.filter(child => child.type === itemType.INTERNAL),
412
- parent: itemPage.id,
413
- parentNavItem: itemNav,
450
+ items: itemChilds.filter(child => child.type === itemType.INTERNAL),
451
+ parent: itemPage.id,
452
+ parentNavItem: itemNav,
414
453
  contentTypes,
415
454
  });
416
455
  pages = {
@@ -430,7 +469,7 @@ module.exports = {
430
469
  };
431
470
  },
432
471
 
433
- renderRFRPage: ({ item, parent }) => {
472
+ renderRFRPage({ item, parent }) {
434
473
  const { uiRouterKey, title, path, slug, related, type, audience, menuAttached } = item;
435
474
  const { __contentType, id, __templateName } = related || {};
436
475
  const contentType = __contentType || '';
@@ -439,7 +478,7 @@ module.exports = {
439
478
  title,
440
479
  templateName: __templateName,
441
480
  related: type === itemType.INTERNAL ? {
442
- contentType: kebabCase(contentType),
481
+ contentType,
443
482
  id,
444
483
  } : undefined,
445
484
  path,
@@ -450,7 +489,7 @@ module.exports = {
450
489
  };
451
490
  },
452
491
 
453
- renderRFRNav: (item) => {
492
+ renderRFRNav(item) {
454
493
  const { uiRouterKey, title, path, type, audience } = item;
455
494
  return {
456
495
  label: title,
@@ -461,19 +500,18 @@ module.exports = {
461
500
  };
462
501
  },
463
502
 
464
- createBranch: (items = [], masterEntity = null, parentItem = null, operations = {}) => {
503
+ createBranch(items = [], masterEntity = null, parentItem = null, operations = {}) {
465
504
  const { pluginName, itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
466
505
  return Promise.all(
467
506
  items.map(async (item) => {
468
507
  operations.create = true;
469
508
  const { parent, master, related, ...params } = item;
470
- const relatedItem =
471
- isNil(related) || params.type === itemType.EXTERNAL ? [] : related;
509
+ const relatedItems = await this.getIdsRelated(related, master);
472
510
  const navigationItem = await strapi
473
511
  .query(itemModel.modelName, pluginName)
474
512
  .create({
475
513
  ...params,
476
- related: isArray(relatedItem) ? relatedItem : [relatedItem],
514
+ related: relatedItems,
477
515
  master: masterEntity,
478
516
  parent: parentItem ? { ...parentItem, _id: parentItem.id } : null,
479
517
  });
@@ -489,17 +527,20 @@ module.exports = {
489
527
  );
490
528
  },
491
529
 
492
- removeBranch: (items = [], operations = {}) => {
530
+ removeBranch(items = [], operations = {}) {
493
531
  const { pluginName, itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
494
532
  return Promise.all(
495
533
  items
496
534
  .filter(item => item.id)
497
535
  .map(async (item) => {
498
536
  operations.remove = true;
499
- const { id } = item;
500
- await strapi
501
- .query(itemModel.modelName, pluginName)
502
- .delete({ id });
537
+ const { id, related, master } = item;
538
+ await Promise.all([
539
+ strapi
540
+ .query(itemModel.modelName, pluginName)
541
+ .delete({ id }),
542
+ this.removeRelated(related, master),
543
+ ]);
503
544
  return !isEmpty(item.items)
504
545
  ? service.removeBranch(
505
546
  item.items,
@@ -510,7 +551,7 @@ module.exports = {
510
551
  );
511
552
  },
512
553
 
513
- updateBranch: async (toUpdate, masterEntity, parentItem, operations) => {
554
+ async updateBranch(toUpdate, masterEntity, parentItem, operations) {
514
555
  const { pluginName, itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
515
556
  const databaseModel = strapi.query(itemModel.modelName, pluginName);
516
557
  return Promise.all(
@@ -519,21 +560,13 @@ module.exports = {
519
560
  const { id, updated, parent, master, related, items, ...params } = item;
520
561
  let currentItem;
521
562
  if (updated) {
522
- const relatedItem =
523
- isNil(related) || params.type === itemType.EXTERNAL
524
- ? []
525
- : related;
526
- await databaseModel
527
- .update(
528
- { id },
529
- { related: [] },
530
- ); // clearing the relation to get it updated properly and not duplicate _morph records
563
+ const relatedItems = await this.getIdsRelated(related, master);
531
564
  currentItem = await databaseModel
532
565
  .update(
533
566
  { id },
534
567
  {
535
568
  ...params,
536
- related: isArray(relatedItem) ? relatedItem : [relatedItem],
569
+ related: relatedItems,
537
570
  master: masterEntity,
538
571
  parent: parentItem ? { ...parentItem, _id: parentItem.id } : null,
539
572
  },
@@ -552,7 +585,7 @@ module.exports = {
552
585
  }),
553
586
  );
554
587
  },
555
- getBranchName: (item) => {
588
+ getBranchName(item) {
556
589
  const hasId = !isNil(item.id);
557
590
  const toRemove = item.removed;
558
591
  if (hasId && !toRemove) {
@@ -566,7 +599,7 @@ module.exports = {
566
599
  }
567
600
  },
568
601
 
569
- analyzeBranch: (items = [], masterEntity = null, parentItem = null, prevOperations = {}) => {
602
+ analyzeBranch(items = [], masterEntity = null, parentItem = null, prevOperations = {}) {
570
603
  const { service } = utilsFunctions.extractMeta(strapi.plugins);
571
604
  const { toCreate, toRemove, toUpdate } = items
572
605
  .reduce((acc, _) => {
@@ -593,7 +626,96 @@ module.exports = {
593
626
  ));
594
627
  },
595
628
 
596
- sanitizeTreeStructure: (entity) => {
629
+ async getRelatedItems(entityItems) {
630
+ const relatedTypes = new Set(entityItems.flatMap((item) => map(item.related, 'relatedType')));
631
+ const groupedItems = Array.from(relatedTypes).reduce(
632
+ (acc, relatedType) => Object.assign(acc, {
633
+ [relatedType]: [
634
+ ...(acc[relatedType] || []),
635
+ ...entityItems
636
+ .filter((item => item?.related.some(related => related.relatedType === relatedType)))
637
+ .flatMap((item) => item.related.map(related => Object.assign(related, { navigationItemId: item.id }))),
638
+ ],
639
+ }),
640
+ {});
641
+
642
+ const data = new Map(
643
+ (
644
+ await Promise.all(
645
+ Object.entries(groupedItems)
646
+ .map(async ([model, related]) => {
647
+ const relationData = await strapi.query(model).find({ id_in: map(related, 'relatedId') });
648
+ return relationData
649
+ .flatMap(_ =>
650
+ Object.assign(
651
+ _,
652
+ {
653
+ __contentType: model,
654
+ navigationItemId: related.find(
655
+ ({ relatedId }) => relatedId === _.id.toString())?.navigationItemId,
656
+ },
657
+ ),
658
+ );
659
+ }),
660
+ )
661
+ )
662
+ .flat(1)
663
+ .map(_ => [_.navigationItemId, _]),
664
+ );
665
+ return entityItems
666
+ .map(({ related, ...item }) => {
667
+ const relatedData = data.get(item.id);
668
+ if (relatedData) {
669
+ return Object.assign(item, { related: [relatedData] });
670
+ }
671
+ return item;
672
+ });
673
+ },
674
+
675
+ getIdsRelated(relatedItems, master) {
676
+ if (relatedItems) {
677
+ return Promise.all(relatedItems.map(async relatedItem => {
678
+ try {
679
+ const query = {
680
+ related_id: relatedItem.refId,
681
+ related_type: relatedItem.ref,
682
+ field: relatedItem.field,
683
+ master,
684
+ };
685
+ const model = strapi.query('navigations_items_related', 'navigation');
686
+ const entity = await model
687
+ .findOne(query);
688
+ if (!entity) {
689
+ const newEntity = {
690
+ master,
691
+ order: 1,
692
+ field: relatedItem.field,
693
+ related_id: relatedItem.refId,
694
+ related_type: relatedItem.ref,
695
+ };
696
+ return model.create(newEntity).then(({ id }) => id);
697
+ }
698
+ return entity.id;
699
+ } catch (e) {
700
+ console.error(e);
701
+ }
702
+ }));
703
+ }
704
+ },
705
+
706
+ removeRelated(relatedItems, master) {
707
+ return Promise.all(relatedItems.map(relatedItem => {
708
+ const model = strapi.query('navigations_items_related', 'navigation');
709
+ const entityToRemove = {
710
+ master,
711
+ field: relatedItem.field,
712
+ related_id: relatedItem.refId,
713
+ related_type: relatedItem.ref,
714
+ };
715
+ return model.delete(entityToRemove).then(({ id }) => id);
716
+ }));
717
+ },
718
+ sanitizeTreeStructure(entity) {
597
719
  const { masterModel, itemModel } = utilsFunctions.extractMeta(strapi.plugins);
598
720
  return sanitizeEntity(
599
721
  {