@fourlights/strapi-plugin-deep-populate 1.3.1 → 1.4.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.
- package/README.md +31 -1
- package/dist/server/index.js +99 -11
- package/dist/server/index.mjs +115 -29
- package/dist/server/src/config/index.d.ts +20 -1
- package/dist/server/src/index.d.ts +3 -1
- package/dist/server/src/services/index.d.ts +1 -0
- package/dist/server/src/services/populate.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ It does not impose a limit on the level of nesting and can cache the populate ob
|
|
|
12
12
|
- Handles circular references and edge cases
|
|
13
13
|
- Includes caching for improved performance
|
|
14
14
|
- Honors `populateCreatorFields` setting
|
|
15
|
+
- Supports optional allow/deny lists for specific relations or components during population
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
@@ -72,7 +73,36 @@ The plugin caches populate objects to improve performance. Cache can be disabled
|
|
|
72
73
|
|
|
73
74
|
The plugin automatically populates `createdBy` and `updatedBy` fields when `populateCreatorFields` is enabled in the content-type configuration.
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
### Allow / Deny Lists
|
|
77
|
+
|
|
78
|
+
Sometimes you may want to restrict the nested population of certain relations or components. For example if you have a `Page` contentType where a deeply nested `Link` component has a relation to another `Page`.
|
|
79
|
+
In those situations you can use the allow or deny lists to control where the plugin should stop resolving nested relations.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// config/plugins.js
|
|
83
|
+
module.exports = ({ env }) => ({
|
|
84
|
+
'deep-populate': {
|
|
85
|
+
enabled: true,
|
|
86
|
+
config: {
|
|
87
|
+
useCache: true,
|
|
88
|
+
replaceWildcard: true,
|
|
89
|
+
|
|
90
|
+
contentTypes: {
|
|
91
|
+
'api::page.page': {
|
|
92
|
+
deny: {
|
|
93
|
+
relations: ['api::page.page'] // prevent resolving nested pages when populating a page
|
|
94
|
+
// alternatively we could have denied the link component in this case
|
|
95
|
+
// components: ['shared.link']
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## How The Plugin Works
|
|
76
106
|
|
|
77
107
|
The plugin recursively:
|
|
78
108
|
1. Traverses the content-type schema
|
package/dist/server/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
const isEmpty$1 = require("lodash/isEmpty");
|
|
3
|
+
const isObject$4 = require("lodash/isObject");
|
|
2
4
|
const ___default = require("lodash");
|
|
3
5
|
const fp = require("lodash/fp");
|
|
4
6
|
const require$$1 = require("crypto");
|
|
@@ -23,6 +25,8 @@ const get = require("lodash/get");
|
|
|
23
25
|
const mergeWith = require("lodash/mergeWith");
|
|
24
26
|
const set$2 = require("lodash/set");
|
|
25
27
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
28
|
+
const isEmpty__default = /* @__PURE__ */ _interopDefault(isEmpty$1);
|
|
29
|
+
const isObject__default = /* @__PURE__ */ _interopDefault(isObject$4);
|
|
26
30
|
const ___default__default = /* @__PURE__ */ _interopDefault(___default);
|
|
27
31
|
const require$$1__default = /* @__PURE__ */ _interopDefault(require$$1);
|
|
28
32
|
const require$$0__default = /* @__PURE__ */ _interopDefault(require$$0$1);
|
|
@@ -45,8 +49,43 @@ const get__default = /* @__PURE__ */ _interopDefault(get);
|
|
|
45
49
|
const mergeWith__default = /* @__PURE__ */ _interopDefault(mergeWith);
|
|
46
50
|
const set__default = /* @__PURE__ */ _interopDefault(set$2);
|
|
47
51
|
const config = {
|
|
48
|
-
default: ({ env: env2 }) => ({ useCache: true, replaceWildcard: true }),
|
|
52
|
+
default: ({ env: env2 }) => ({ useCache: true, replaceWildcard: true, contentTypes: {} }),
|
|
49
53
|
validator: (config2) => {
|
|
54
|
+
if (!isObject__default.default(config2.contentTypes)) {
|
|
55
|
+
throw new Error("plugin::deep-populate config.contentTypes must be an object");
|
|
56
|
+
}
|
|
57
|
+
if (!isEmpty__default.default(config2.contentTypes)) {
|
|
58
|
+
for (const [uid, contentTypeConfig] of Object.entries(config2.contentTypes)) {
|
|
59
|
+
if (!isObject__default.default(contentTypeConfig)) {
|
|
60
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid} must be an object`);
|
|
61
|
+
}
|
|
62
|
+
if (!contentTypeConfig.allow && !contentTypeConfig.deny) {
|
|
63
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid} must have an "allow" or "deny".`);
|
|
64
|
+
}
|
|
65
|
+
if (contentTypeConfig.allow && !isObject__default.default(contentTypeConfig.allow)) {
|
|
66
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.allow must be an object`);
|
|
67
|
+
}
|
|
68
|
+
if (contentTypeConfig.deny && !isObject__default.default(contentTypeConfig.deny)) {
|
|
69
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.deny must be an object`);
|
|
70
|
+
}
|
|
71
|
+
if (contentTypeConfig.allow) {
|
|
72
|
+
if (contentTypeConfig.allow.relations && !Array.isArray(contentTypeConfig.allow.relations)) {
|
|
73
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.allow.relations must be an array`);
|
|
74
|
+
}
|
|
75
|
+
if (contentTypeConfig.allow.components && !Array.isArray(contentTypeConfig.allow.components)) {
|
|
76
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.allow.components must be an array`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (contentTypeConfig.deny) {
|
|
80
|
+
if (contentTypeConfig.deny.relations && !Array.isArray(contentTypeConfig.deny.relations)) {
|
|
81
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.deny.relations must be an array`);
|
|
82
|
+
}
|
|
83
|
+
if (contentTypeConfig.deny.components && !Array.isArray(contentTypeConfig.deny.components)) {
|
|
84
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.deny.components must be an array`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
50
89
|
}
|
|
51
90
|
};
|
|
52
91
|
const schema$1 = {
|
|
@@ -14573,7 +14612,7 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
14573
14612
|
});
|
|
14574
14613
|
};
|
|
14575
14614
|
const getHash = (params) => {
|
|
14576
|
-
return `${params.contentType}-${params.documentId}-${params.locale}-${params.status}-${params.omitEmpty ? "sparse" : "full"}`;
|
|
14615
|
+
return `${params.contentType}-${params.documentId}-${params.locale}-${params.status}-${params.omitEmpty ? "sparse" : "full"}-${params.localizations ? "all" : "single"}`;
|
|
14577
14616
|
};
|
|
14578
14617
|
const cache = ({ strapi: strapi2 }) => ({
|
|
14579
14618
|
async get(params) {
|
|
@@ -14636,18 +14675,26 @@ const hasValue = (value) => {
|
|
|
14636
14675
|
return !(value === null || value === void 0 || Array.isArray(value) && value.length === 0 || typeof value === "object" && isEmpty(value));
|
|
14637
14676
|
};
|
|
14638
14677
|
async function _populateComponent({
|
|
14678
|
+
schema: schema2,
|
|
14639
14679
|
populate: populate2 = {},
|
|
14640
14680
|
lookup,
|
|
14641
14681
|
attrName,
|
|
14642
14682
|
inDynamicZone = false,
|
|
14683
|
+
__allow,
|
|
14684
|
+
__deny,
|
|
14643
14685
|
...params
|
|
14644
14686
|
}) {
|
|
14645
14687
|
const componentLookup = lookup.length === 0 ? [attrName] : [...lookup, inDynamicZone ? "on" : "populate", attrName];
|
|
14646
14688
|
const componentPopulate = populate2;
|
|
14647
14689
|
set__default.default(componentPopulate, componentLookup, { populate: "*" });
|
|
14690
|
+
if (__allow?.components && !__allow.components.includes(schema2)) return { populate: "*" };
|
|
14691
|
+
if (__deny?.components?.includes(schema2)) return { populate: "*" };
|
|
14648
14692
|
const nestedPopulate = await _populate({
|
|
14693
|
+
schema: schema2,
|
|
14649
14694
|
populate: componentPopulate,
|
|
14650
14695
|
lookup: componentLookup,
|
|
14696
|
+
__allow,
|
|
14697
|
+
__deny,
|
|
14651
14698
|
...params
|
|
14652
14699
|
});
|
|
14653
14700
|
return isEmpty(nestedPopulate) ? true : { populate: nestedPopulate };
|
|
@@ -14680,9 +14727,7 @@ async function _populateRelation({
|
|
|
14680
14727
|
contentType,
|
|
14681
14728
|
relation,
|
|
14682
14729
|
resolvedRelations,
|
|
14683
|
-
|
|
14684
|
-
locale: locale2,
|
|
14685
|
-
status: status2
|
|
14730
|
+
...params
|
|
14686
14731
|
}) {
|
|
14687
14732
|
const isSingleRelation = !Array.isArray(relation);
|
|
14688
14733
|
const relations = isSingleRelation ? [relation] : relation;
|
|
@@ -14694,9 +14739,7 @@ async function _populateRelation({
|
|
|
14694
14739
|
documentId: relation2.documentId,
|
|
14695
14740
|
schema: contentType,
|
|
14696
14741
|
resolvedRelations,
|
|
14697
|
-
|
|
14698
|
-
locale: locale2,
|
|
14699
|
-
status: status2
|
|
14742
|
+
...params
|
|
14700
14743
|
});
|
|
14701
14744
|
resolvedRelations.set(relation2.documentId, relationPopulate);
|
|
14702
14745
|
}
|
|
@@ -14746,6 +14789,8 @@ async function _populate({
|
|
|
14746
14789
|
lookup = [],
|
|
14747
14790
|
resolvedRelations,
|
|
14748
14791
|
omitEmpty,
|
|
14792
|
+
__deny,
|
|
14793
|
+
__allow,
|
|
14749
14794
|
...params
|
|
14750
14795
|
}) {
|
|
14751
14796
|
const newPopulate = {};
|
|
@@ -14771,9 +14816,33 @@ async function _populate({
|
|
|
14771
14816
|
for (const [attrName, attr] of relations) {
|
|
14772
14817
|
const value = _resolveValue({ document: document2, attrName, lookup });
|
|
14773
14818
|
if (!hasValue(value)) {
|
|
14774
|
-
if (
|
|
14819
|
+
if (omitEmpty !== true) newPopulate[attrName] = true;
|
|
14775
14820
|
continue;
|
|
14776
14821
|
}
|
|
14822
|
+
if (params.localizations !== true && attrName === "localizations" && hasValue(value)) {
|
|
14823
|
+
newPopulate[attrName] = true;
|
|
14824
|
+
continue;
|
|
14825
|
+
}
|
|
14826
|
+
if (contentTypes.isRelationalAttribute(attr)) {
|
|
14827
|
+
if (__allow?.relations && !__allow.relations.includes(attr.target)) {
|
|
14828
|
+
newPopulate[attrName] = true;
|
|
14829
|
+
continue;
|
|
14830
|
+
}
|
|
14831
|
+
if (__deny?.relations?.includes(attr.target)) {
|
|
14832
|
+
newPopulate[attrName] = true;
|
|
14833
|
+
continue;
|
|
14834
|
+
}
|
|
14835
|
+
}
|
|
14836
|
+
if (contentTypes.isComponentAttribute(attr) && !contentTypes.isDynamicZoneAttribute(attr)) {
|
|
14837
|
+
if (__allow?.components && !__allow.components.includes(attr.component)) {
|
|
14838
|
+
newPopulate[attrName] = true;
|
|
14839
|
+
continue;
|
|
14840
|
+
}
|
|
14841
|
+
if (__deny?.components?.includes(attr.component)) {
|
|
14842
|
+
newPopulate[attrName] = true;
|
|
14843
|
+
continue;
|
|
14844
|
+
}
|
|
14845
|
+
}
|
|
14777
14846
|
resolveRelations.push([attrName, attr, value]);
|
|
14778
14847
|
}
|
|
14779
14848
|
relations = null;
|
|
@@ -14790,6 +14859,8 @@ async function _populate({
|
|
|
14790
14859
|
attrName,
|
|
14791
14860
|
resolvedRelations,
|
|
14792
14861
|
omitEmpty,
|
|
14862
|
+
__deny,
|
|
14863
|
+
__allow,
|
|
14793
14864
|
...params
|
|
14794
14865
|
});
|
|
14795
14866
|
}
|
|
@@ -14800,7 +14871,10 @@ async function _populate({
|
|
|
14800
14871
|
resolvedRelations,
|
|
14801
14872
|
omitEmpty,
|
|
14802
14873
|
locale: params.locale,
|
|
14803
|
-
status: params.status
|
|
14874
|
+
status: params.status,
|
|
14875
|
+
localizations: params.localizations,
|
|
14876
|
+
__deny,
|
|
14877
|
+
__allow
|
|
14804
14878
|
});
|
|
14805
14879
|
}
|
|
14806
14880
|
if (contentTypes.isComponentAttribute(attr) && !contentTypes.isDynamicZoneAttribute(attr)) {
|
|
@@ -14811,6 +14885,8 @@ async function _populate({
|
|
|
14811
14885
|
attrName,
|
|
14812
14886
|
resolvedRelations,
|
|
14813
14887
|
omitEmpty,
|
|
14888
|
+
__deny,
|
|
14889
|
+
__allow,
|
|
14814
14890
|
...params
|
|
14815
14891
|
});
|
|
14816
14892
|
}
|
|
@@ -14821,8 +14897,20 @@ async function _populate({
|
|
|
14821
14897
|
return newPopulate;
|
|
14822
14898
|
}
|
|
14823
14899
|
async function populate$1(params) {
|
|
14900
|
+
const { contentTypes: contentTypes2 } = strapi.config.get("plugin::deep-populate");
|
|
14901
|
+
const contentTypeConfig = has__default.default(contentTypes2, "*") ? get__default.default(contentTypes2, "*") : {};
|
|
14902
|
+
if (has__default.default(contentTypes2, params.contentType)) {
|
|
14903
|
+
mergeWith__default.default(contentTypeConfig, get__default.default(contentTypes2, params.contentType));
|
|
14904
|
+
}
|
|
14905
|
+
const { allow, deny } = contentTypeConfig;
|
|
14824
14906
|
const resolvedRelations = /* @__PURE__ */ new Map();
|
|
14825
|
-
const populated = await _populate({
|
|
14907
|
+
const populated = await _populate({
|
|
14908
|
+
...params,
|
|
14909
|
+
schema: params.contentType,
|
|
14910
|
+
resolvedRelations,
|
|
14911
|
+
__deny: deny,
|
|
14912
|
+
__allow: allow
|
|
14913
|
+
});
|
|
14826
14914
|
return { populate: populated, dependencies: [...resolvedRelations.keys()] };
|
|
14827
14915
|
}
|
|
14828
14916
|
const populate = ({ strapi: strapi2 }) => ({
|
package/dist/server/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import isEmpty$1 from "lodash/isEmpty";
|
|
2
|
+
import isObject$4 from "lodash/isObject";
|
|
1
3
|
import ___default from "lodash";
|
|
2
|
-
import { curry, isArray, isObject as isObject$
|
|
4
|
+
import { curry, isArray, isObject as isObject$5, isEmpty as isEmpty$2, cloneDeep, omit, isNil, trim as trim$1, isString, pipe as pipe$1, split as split$1, map as map$2, flatten, first, identity, constant, join, eq, clone as clone$3, get, pick, has as has$1, union, getOr, toPath, isBoolean as isBoolean$1 } from "lodash/fp";
|
|
3
5
|
import require$$1 from "crypto";
|
|
4
6
|
import require$$0$1 from "child_process";
|
|
5
7
|
import has from "lodash/has";
|
|
@@ -22,8 +24,43 @@ import get$1 from "lodash/get";
|
|
|
22
24
|
import mergeWith from "lodash/mergeWith";
|
|
23
25
|
import set$2 from "lodash/set";
|
|
24
26
|
const config = {
|
|
25
|
-
default: ({ env: env2 }) => ({ useCache: true, replaceWildcard: true }),
|
|
27
|
+
default: ({ env: env2 }) => ({ useCache: true, replaceWildcard: true, contentTypes: {} }),
|
|
26
28
|
validator: (config2) => {
|
|
29
|
+
if (!isObject$4(config2.contentTypes)) {
|
|
30
|
+
throw new Error("plugin::deep-populate config.contentTypes must be an object");
|
|
31
|
+
}
|
|
32
|
+
if (!isEmpty$1(config2.contentTypes)) {
|
|
33
|
+
for (const [uid, contentTypeConfig] of Object.entries(config2.contentTypes)) {
|
|
34
|
+
if (!isObject$4(contentTypeConfig)) {
|
|
35
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid} must be an object`);
|
|
36
|
+
}
|
|
37
|
+
if (!contentTypeConfig.allow && !contentTypeConfig.deny) {
|
|
38
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid} must have an "allow" or "deny".`);
|
|
39
|
+
}
|
|
40
|
+
if (contentTypeConfig.allow && !isObject$4(contentTypeConfig.allow)) {
|
|
41
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.allow must be an object`);
|
|
42
|
+
}
|
|
43
|
+
if (contentTypeConfig.deny && !isObject$4(contentTypeConfig.deny)) {
|
|
44
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.deny must be an object`);
|
|
45
|
+
}
|
|
46
|
+
if (contentTypeConfig.allow) {
|
|
47
|
+
if (contentTypeConfig.allow.relations && !Array.isArray(contentTypeConfig.allow.relations)) {
|
|
48
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.allow.relations must be an array`);
|
|
49
|
+
}
|
|
50
|
+
if (contentTypeConfig.allow.components && !Array.isArray(contentTypeConfig.allow.components)) {
|
|
51
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.allow.components must be an array`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (contentTypeConfig.deny) {
|
|
55
|
+
if (contentTypeConfig.deny.relations && !Array.isArray(contentTypeConfig.deny.relations)) {
|
|
56
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.deny.relations must be an array`);
|
|
57
|
+
}
|
|
58
|
+
if (contentTypeConfig.deny.components && !Array.isArray(contentTypeConfig.deny.components)) {
|
|
59
|
+
throw new Error(`plugin::deep-populate config.contentTypes.${uid}.deny.components must be an array`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
27
64
|
}
|
|
28
65
|
};
|
|
29
66
|
const schema$1 = {
|
|
@@ -13117,7 +13154,7 @@ const traverseEntity = async (visitor2, options, entity) => {
|
|
|
13117
13154
|
const traverseOptions = { schema: targetSchema, path: path22, getModel, parent };
|
|
13118
13155
|
return traverseEntity(visitor22, traverseOptions, entry);
|
|
13119
13156
|
};
|
|
13120
|
-
if (!isObject$
|
|
13157
|
+
if (!isObject$5(entity) || isNil(schema2)) {
|
|
13121
13158
|
return entity;
|
|
13122
13159
|
}
|
|
13123
13160
|
const copy = clone$3(entity);
|
|
@@ -13353,7 +13390,7 @@ const removeRestrictedRelations = (auth) => async ({ data, key, attribute, schem
|
|
|
13353
13390
|
return allowedElements;
|
|
13354
13391
|
}
|
|
13355
13392
|
for (const element of elements) {
|
|
13356
|
-
if (!isObject$
|
|
13393
|
+
if (!isObject$5(element) || !("__type" in element)) {
|
|
13357
13394
|
continue;
|
|
13358
13395
|
}
|
|
13359
13396
|
const scopes = ACTIONS_TO_VERIFY$1.map((action) => `${element.__type}.${action}`);
|
|
@@ -13586,7 +13623,7 @@ const traverseFactory = () => {
|
|
|
13586
13623
|
}
|
|
13587
13624
|
};
|
|
13588
13625
|
};
|
|
13589
|
-
const isObj$2 = (value) => isObject$
|
|
13626
|
+
const isObj$2 = (value) => isObject$5(value);
|
|
13590
13627
|
const filters = traverseFactory().intercept(
|
|
13591
13628
|
// Intercept filters arrays and apply the traversal to each one individually
|
|
13592
13629
|
isArray,
|
|
@@ -13597,11 +13634,11 @@ const filters = traverseFactory().intercept(
|
|
|
13597
13634
|
return recurse(visitor2, { ...options, path: newPath }, filter);
|
|
13598
13635
|
})
|
|
13599
13636
|
// todo: move that to the visitors
|
|
13600
|
-
).then((res) => res.filter((val) => !(isObject$
|
|
13637
|
+
).then((res) => res.filter((val) => !(isObject$5(val) && isEmpty$2(val))));
|
|
13601
13638
|
}
|
|
13602
13639
|
).intercept(
|
|
13603
13640
|
// Ignore non object filters and return the value as-is
|
|
13604
|
-
(filters2) => !isObject$
|
|
13641
|
+
(filters2) => !isObject$5(filters2),
|
|
13605
13642
|
(_2, __, filters2) => {
|
|
13606
13643
|
return filters2;
|
|
13607
13644
|
}
|
|
@@ -13668,23 +13705,23 @@ const ORDERS = { asc: "asc", desc: "desc" };
|
|
|
13668
13705
|
const ORDER_VALUES = Object.values(ORDERS);
|
|
13669
13706
|
const isSortOrder = (value) => ORDER_VALUES.includes(value.toLowerCase());
|
|
13670
13707
|
const isStringArray$2 = (value) => Array.isArray(value) && value.every(isString);
|
|
13671
|
-
const isObjectArray = (value) => Array.isArray(value) && value.every(isObject$
|
|
13708
|
+
const isObjectArray = (value) => Array.isArray(value) && value.every(isObject$5);
|
|
13672
13709
|
const isNestedSorts = (value) => isString(value) && value.split(",").length > 1;
|
|
13673
|
-
const isObj$1 = (value) => isObject$
|
|
13710
|
+
const isObj$1 = (value) => isObject$5(value);
|
|
13674
13711
|
const sort = traverseFactory().intercept(
|
|
13675
13712
|
// String with chained sorts (foo,bar,foobar) => split, map(recurse), then recompose
|
|
13676
13713
|
isNestedSorts,
|
|
13677
13714
|
async (visitor2, options, sort2, { recurse }) => {
|
|
13678
13715
|
return Promise.all(
|
|
13679
13716
|
sort2.split(",").map(trim$1).map((nestedSort) => recurse(visitor2, options, nestedSort))
|
|
13680
|
-
).then((res) => res.filter((part) => !isEmpty$
|
|
13717
|
+
).then((res) => res.filter((part) => !isEmpty$2(part)).join(","));
|
|
13681
13718
|
}
|
|
13682
13719
|
).intercept(
|
|
13683
13720
|
// Array of strings ['foo', 'foo,bar'] => map(recurse), then filter out empty items
|
|
13684
13721
|
isStringArray$2,
|
|
13685
13722
|
async (visitor2, options, sort2, { recurse }) => {
|
|
13686
13723
|
return Promise.all(sort2.map((nestedSort) => recurse(visitor2, options, nestedSort))).then(
|
|
13687
|
-
(res) => res.filter((nestedSort) => !isEmpty$
|
|
13724
|
+
(res) => res.filter((nestedSort) => !isEmpty$2(nestedSort))
|
|
13688
13725
|
);
|
|
13689
13726
|
}
|
|
13690
13727
|
).intercept(
|
|
@@ -13692,7 +13729,7 @@ const sort = traverseFactory().intercept(
|
|
|
13692
13729
|
isObjectArray,
|
|
13693
13730
|
async (visitor2, options, sort2, { recurse }) => {
|
|
13694
13731
|
return Promise.all(sort2.map((nestedSort) => recurse(visitor2, options, nestedSort))).then(
|
|
13695
|
-
(res) => res.filter((nestedSort) => !isEmpty$
|
|
13732
|
+
(res) => res.filter((nestedSort) => !isEmpty$2(nestedSort))
|
|
13696
13733
|
);
|
|
13697
13734
|
}
|
|
13698
13735
|
).parse(isString, () => {
|
|
@@ -13702,7 +13739,7 @@ const sort = traverseFactory().intercept(
|
|
|
13702
13739
|
return void 0;
|
|
13703
13740
|
}
|
|
13704
13741
|
return parts.reduce((acc, part) => {
|
|
13705
|
-
if (isEmpty$
|
|
13742
|
+
if (isEmpty$2(part)) {
|
|
13706
13743
|
return acc;
|
|
13707
13744
|
}
|
|
13708
13745
|
if (acc === "") {
|
|
@@ -13797,7 +13834,7 @@ const isPopulateString = (value) => {
|
|
|
13797
13834
|
return isString(value) && !isWildcard(value);
|
|
13798
13835
|
};
|
|
13799
13836
|
const isStringArray$1 = (value) => isArray(value) && value.every(isString);
|
|
13800
|
-
const isObj = (value) => isObject$
|
|
13837
|
+
const isObj = (value) => isObject$5(value);
|
|
13801
13838
|
const populate$2 = traverseFactory().intercept(isPopulateString, async (visitor2, options, populate2, { recurse }) => {
|
|
13802
13839
|
const populateObject = pathsToObjectPopulate([populate2]);
|
|
13803
13840
|
const traversedPopulate = await recurse(visitor2, options, populateObject);
|
|
@@ -13846,7 +13883,7 @@ const populate$2 = traverseFactory().intercept(isPopulateString, async (visitor2
|
|
|
13846
13883
|
if (root2 !== key) {
|
|
13847
13884
|
return data;
|
|
13848
13885
|
}
|
|
13849
|
-
return isNil(value) || isEmpty$
|
|
13886
|
+
return isNil(value) || isEmpty$2(value) ? root2 : `${root2}.${value}`;
|
|
13850
13887
|
},
|
|
13851
13888
|
keys(data) {
|
|
13852
13889
|
const v = first(tokenize(data));
|
|
@@ -13907,7 +13944,7 @@ const populate$2 = traverseFactory().intercept(isPopulateString, async (visitor2
|
|
|
13907
13944
|
}
|
|
13908
13945
|
const parent = { key, path: path2, schema: schema2, attribute };
|
|
13909
13946
|
if (isMorphToRelationalAttribute(attribute)) {
|
|
13910
|
-
if (!isObject$
|
|
13947
|
+
if (!isObject$5(value) || !("on" in value && isObject$5(value?.on))) {
|
|
13911
13948
|
return;
|
|
13912
13949
|
}
|
|
13913
13950
|
const newValue2 = await recurse(
|
|
@@ -13956,7 +13993,7 @@ const populate$2 = traverseFactory().intercept(isPopulateString, async (visitor2
|
|
|
13956
13993
|
}
|
|
13957
13994
|
).onDynamicZone(
|
|
13958
13995
|
async ({ key, value, schema: schema2, visitor: visitor2, path: path2, attribute, getModel }, { set: set2, recurse }) => {
|
|
13959
|
-
if (isNil(value) || !isObject$
|
|
13996
|
+
if (isNil(value) || !isObject$5(value)) {
|
|
13960
13997
|
return;
|
|
13961
13998
|
}
|
|
13962
13999
|
const parent = { key, path: path2, schema: schema2, attribute };
|
|
@@ -14069,7 +14106,7 @@ const defaultSanitizeFilters = curry((ctx, filters2) => {
|
|
|
14069
14106
|
traverseQueryFilters(visitor$7, ctx),
|
|
14070
14107
|
// Remove empty objects
|
|
14071
14108
|
traverseQueryFilters(({ key, value }, { remove: remove2 }) => {
|
|
14072
|
-
if (isObject$
|
|
14109
|
+
if (isObject$5(value) && isEmpty$2(value)) {
|
|
14073
14110
|
remove2(key);
|
|
14074
14111
|
}
|
|
14075
14112
|
}, ctx)
|
|
@@ -14102,7 +14139,7 @@ const defaultSanitizeSort = curry((ctx, sort2) => {
|
|
|
14102
14139
|
if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
|
|
14103
14140
|
return;
|
|
14104
14141
|
}
|
|
14105
|
-
if (!isScalarAttribute(attribute) && isEmpty$
|
|
14142
|
+
if (!isScalarAttribute(attribute) && isEmpty$2(value)) {
|
|
14106
14143
|
remove2(key);
|
|
14107
14144
|
}
|
|
14108
14145
|
}, ctx)
|
|
@@ -14550,7 +14587,7 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
14550
14587
|
});
|
|
14551
14588
|
};
|
|
14552
14589
|
const getHash = (params) => {
|
|
14553
|
-
return `${params.contentType}-${params.documentId}-${params.locale}-${params.status}-${params.omitEmpty ? "sparse" : "full"}`;
|
|
14590
|
+
return `${params.contentType}-${params.documentId}-${params.locale}-${params.status}-${params.omitEmpty ? "sparse" : "full"}-${params.localizations ? "all" : "single"}`;
|
|
14554
14591
|
};
|
|
14555
14592
|
const cache = ({ strapi: strapi2 }) => ({
|
|
14556
14593
|
async get(params) {
|
|
@@ -14613,18 +14650,26 @@ const hasValue = (value) => {
|
|
|
14613
14650
|
return !(value === null || value === void 0 || Array.isArray(value) && value.length === 0 || typeof value === "object" && isEmpty(value));
|
|
14614
14651
|
};
|
|
14615
14652
|
async function _populateComponent({
|
|
14653
|
+
schema: schema2,
|
|
14616
14654
|
populate: populate2 = {},
|
|
14617
14655
|
lookup,
|
|
14618
14656
|
attrName,
|
|
14619
14657
|
inDynamicZone = false,
|
|
14658
|
+
__allow,
|
|
14659
|
+
__deny,
|
|
14620
14660
|
...params
|
|
14621
14661
|
}) {
|
|
14622
14662
|
const componentLookup = lookup.length === 0 ? [attrName] : [...lookup, inDynamicZone ? "on" : "populate", attrName];
|
|
14623
14663
|
const componentPopulate = populate2;
|
|
14624
14664
|
set$2(componentPopulate, componentLookup, { populate: "*" });
|
|
14665
|
+
if (__allow?.components && !__allow.components.includes(schema2)) return { populate: "*" };
|
|
14666
|
+
if (__deny?.components?.includes(schema2)) return { populate: "*" };
|
|
14625
14667
|
const nestedPopulate = await _populate({
|
|
14668
|
+
schema: schema2,
|
|
14626
14669
|
populate: componentPopulate,
|
|
14627
14670
|
lookup: componentLookup,
|
|
14671
|
+
__allow,
|
|
14672
|
+
__deny,
|
|
14628
14673
|
...params
|
|
14629
14674
|
});
|
|
14630
14675
|
return isEmpty(nestedPopulate) ? true : { populate: nestedPopulate };
|
|
@@ -14657,9 +14702,7 @@ async function _populateRelation({
|
|
|
14657
14702
|
contentType,
|
|
14658
14703
|
relation,
|
|
14659
14704
|
resolvedRelations,
|
|
14660
|
-
|
|
14661
|
-
locale: locale2,
|
|
14662
|
-
status: status2
|
|
14705
|
+
...params
|
|
14663
14706
|
}) {
|
|
14664
14707
|
const isSingleRelation = !Array.isArray(relation);
|
|
14665
14708
|
const relations = isSingleRelation ? [relation] : relation;
|
|
@@ -14671,9 +14714,7 @@ async function _populateRelation({
|
|
|
14671
14714
|
documentId: relation2.documentId,
|
|
14672
14715
|
schema: contentType,
|
|
14673
14716
|
resolvedRelations,
|
|
14674
|
-
|
|
14675
|
-
locale: locale2,
|
|
14676
|
-
status: status2
|
|
14717
|
+
...params
|
|
14677
14718
|
});
|
|
14678
14719
|
resolvedRelations.set(relation2.documentId, relationPopulate);
|
|
14679
14720
|
}
|
|
@@ -14723,6 +14764,8 @@ async function _populate({
|
|
|
14723
14764
|
lookup = [],
|
|
14724
14765
|
resolvedRelations,
|
|
14725
14766
|
omitEmpty,
|
|
14767
|
+
__deny,
|
|
14768
|
+
__allow,
|
|
14726
14769
|
...params
|
|
14727
14770
|
}) {
|
|
14728
14771
|
const newPopulate = {};
|
|
@@ -14748,9 +14791,33 @@ async function _populate({
|
|
|
14748
14791
|
for (const [attrName, attr] of relations) {
|
|
14749
14792
|
const value = _resolveValue({ document: document2, attrName, lookup });
|
|
14750
14793
|
if (!hasValue(value)) {
|
|
14751
|
-
if (
|
|
14794
|
+
if (omitEmpty !== true) newPopulate[attrName] = true;
|
|
14752
14795
|
continue;
|
|
14753
14796
|
}
|
|
14797
|
+
if (params.localizations !== true && attrName === "localizations" && hasValue(value)) {
|
|
14798
|
+
newPopulate[attrName] = true;
|
|
14799
|
+
continue;
|
|
14800
|
+
}
|
|
14801
|
+
if (contentTypes.isRelationalAttribute(attr)) {
|
|
14802
|
+
if (__allow?.relations && !__allow.relations.includes(attr.target)) {
|
|
14803
|
+
newPopulate[attrName] = true;
|
|
14804
|
+
continue;
|
|
14805
|
+
}
|
|
14806
|
+
if (__deny?.relations?.includes(attr.target)) {
|
|
14807
|
+
newPopulate[attrName] = true;
|
|
14808
|
+
continue;
|
|
14809
|
+
}
|
|
14810
|
+
}
|
|
14811
|
+
if (contentTypes.isComponentAttribute(attr) && !contentTypes.isDynamicZoneAttribute(attr)) {
|
|
14812
|
+
if (__allow?.components && !__allow.components.includes(attr.component)) {
|
|
14813
|
+
newPopulate[attrName] = true;
|
|
14814
|
+
continue;
|
|
14815
|
+
}
|
|
14816
|
+
if (__deny?.components?.includes(attr.component)) {
|
|
14817
|
+
newPopulate[attrName] = true;
|
|
14818
|
+
continue;
|
|
14819
|
+
}
|
|
14820
|
+
}
|
|
14754
14821
|
resolveRelations.push([attrName, attr, value]);
|
|
14755
14822
|
}
|
|
14756
14823
|
relations = null;
|
|
@@ -14767,6 +14834,8 @@ async function _populate({
|
|
|
14767
14834
|
attrName,
|
|
14768
14835
|
resolvedRelations,
|
|
14769
14836
|
omitEmpty,
|
|
14837
|
+
__deny,
|
|
14838
|
+
__allow,
|
|
14770
14839
|
...params
|
|
14771
14840
|
});
|
|
14772
14841
|
}
|
|
@@ -14777,7 +14846,10 @@ async function _populate({
|
|
|
14777
14846
|
resolvedRelations,
|
|
14778
14847
|
omitEmpty,
|
|
14779
14848
|
locale: params.locale,
|
|
14780
|
-
status: params.status
|
|
14849
|
+
status: params.status,
|
|
14850
|
+
localizations: params.localizations,
|
|
14851
|
+
__deny,
|
|
14852
|
+
__allow
|
|
14781
14853
|
});
|
|
14782
14854
|
}
|
|
14783
14855
|
if (contentTypes.isComponentAttribute(attr) && !contentTypes.isDynamicZoneAttribute(attr)) {
|
|
@@ -14788,6 +14860,8 @@ async function _populate({
|
|
|
14788
14860
|
attrName,
|
|
14789
14861
|
resolvedRelations,
|
|
14790
14862
|
omitEmpty,
|
|
14863
|
+
__deny,
|
|
14864
|
+
__allow,
|
|
14791
14865
|
...params
|
|
14792
14866
|
});
|
|
14793
14867
|
}
|
|
@@ -14798,8 +14872,20 @@ async function _populate({
|
|
|
14798
14872
|
return newPopulate;
|
|
14799
14873
|
}
|
|
14800
14874
|
async function populate$1(params) {
|
|
14875
|
+
const { contentTypes: contentTypes2 } = strapi.config.get("plugin::deep-populate");
|
|
14876
|
+
const contentTypeConfig = has(contentTypes2, "*") ? get$1(contentTypes2, "*") : {};
|
|
14877
|
+
if (has(contentTypes2, params.contentType)) {
|
|
14878
|
+
mergeWith(contentTypeConfig, get$1(contentTypes2, params.contentType));
|
|
14879
|
+
}
|
|
14880
|
+
const { allow, deny } = contentTypeConfig;
|
|
14801
14881
|
const resolvedRelations = /* @__PURE__ */ new Map();
|
|
14802
|
-
const populated = await _populate({
|
|
14882
|
+
const populated = await _populate({
|
|
14883
|
+
...params,
|
|
14884
|
+
schema: params.contentType,
|
|
14885
|
+
resolvedRelations,
|
|
14886
|
+
__deny: deny,
|
|
14887
|
+
__allow: allow
|
|
14888
|
+
});
|
|
14803
14889
|
return { populate: populated, dependencies: [...resolvedRelations.keys()] };
|
|
14804
14890
|
}
|
|
14805
14891
|
const populate = ({ strapi: strapi2 }) => ({
|
|
@@ -1,10 +1,29 @@
|
|
|
1
|
+
import type { UID } from "@strapi/strapi";
|
|
2
|
+
export type ContentTypeConfigAllow = {
|
|
3
|
+
relations?: UID.ContentType[];
|
|
4
|
+
components?: UID.Component[];
|
|
5
|
+
};
|
|
6
|
+
export type ContentTypeConfigDeny = {
|
|
7
|
+
relations?: UID.ContentType[];
|
|
8
|
+
components?: UID.Component[];
|
|
9
|
+
};
|
|
10
|
+
type ContentTypeConfig = {
|
|
11
|
+
allow?: ContentTypeConfigAllow;
|
|
12
|
+
deny?: ContentTypeConfigDeny;
|
|
13
|
+
};
|
|
14
|
+
export type Config = {
|
|
15
|
+
useCache: boolean;
|
|
16
|
+
replaceWildcard: boolean;
|
|
17
|
+
contentTypes: Record<UID.ContentType | "*", ContentTypeConfig>;
|
|
18
|
+
};
|
|
1
19
|
declare const _default: {
|
|
2
20
|
default: ({ env }: {
|
|
3
21
|
env: any;
|
|
4
22
|
}) => {
|
|
5
23
|
useCache: boolean;
|
|
6
24
|
replaceWildcard: boolean;
|
|
25
|
+
contentTypes: {};
|
|
7
26
|
};
|
|
8
|
-
validator: (config:
|
|
27
|
+
validator: (config: Config) => void;
|
|
9
28
|
};
|
|
10
29
|
export default _default;
|
|
@@ -5,8 +5,9 @@ declare const _default: {
|
|
|
5
5
|
}) => {
|
|
6
6
|
useCache: boolean;
|
|
7
7
|
replaceWildcard: boolean;
|
|
8
|
+
contentTypes: {};
|
|
8
9
|
};
|
|
9
|
-
validator: (config:
|
|
10
|
+
validator: (config: import("./config").Config) => void;
|
|
10
11
|
};
|
|
11
12
|
contentTypes: {
|
|
12
13
|
cache: {
|
|
@@ -72,6 +73,7 @@ declare const _default: {
|
|
|
72
73
|
contentType: import("@strapi/types/dist/uid").ContentType;
|
|
73
74
|
documentId: string;
|
|
74
75
|
omitEmpty?: boolean;
|
|
76
|
+
localizations?: boolean;
|
|
75
77
|
} & {
|
|
76
78
|
populate?: import("@strapi/types/dist/modules/documents/params/populate").Any<import("@strapi/types/dist/uid").ContentType>;
|
|
77
79
|
} & {
|
|
@@ -14,6 +14,7 @@ declare const _default: {
|
|
|
14
14
|
contentType: import("@strapi/types/dist/uid").ContentType;
|
|
15
15
|
documentId: string;
|
|
16
16
|
omitEmpty?: boolean;
|
|
17
|
+
localizations?: boolean;
|
|
17
18
|
} & {
|
|
18
19
|
populate?: import("@strapi/types/dist/modules/documents/params/populate").Any<import("@strapi/types/dist/uid").ContentType>;
|
|
19
20
|
} & {
|
package/package.json
CHANGED