strapi-plugin-populate-all 1.4.1 → 1.5.1-beta.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
CHANGED
|
@@ -51,4 +51,4 @@ module.exports = {
|
|
|
51
51
|
- The plugin provides a global middleware that intercepts requests with `?populate=all` and rewrites the query to trigger recursive population.
|
|
52
52
|
- In the background, it builds a standard Strapi populate query as described in the [Strapi documentation](https://docs.strapi.io/cms/api/rest/populate-select).
|
|
53
53
|
- You can control which relations are included using the relations config option.
|
|
54
|
-
- Inside the document API, you can set `populate: '*'` and `
|
|
54
|
+
- Inside the document API, you can set `populate: '*'` and `_q: "populate-all"` to make it work ("populate-all" can be anywhere inside `_q`, we detect it via .includes)
|
package/dist/server/index.js
CHANGED
|
@@ -1,20 +1,61 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
const PLUGIN_QUERY_DOCUMENT_TAG = "populate-all";
|
|
3
|
+
const config = {
|
|
4
|
+
default: {
|
|
5
|
+
relations: true
|
|
6
|
+
},
|
|
7
|
+
validator(config2 = {}) {
|
|
8
|
+
for (const [key, value] of Object.entries(config2)) {
|
|
9
|
+
switch (key) {
|
|
10
|
+
case "cache": {
|
|
11
|
+
const isBoolean = typeof value === "boolean";
|
|
12
|
+
if (!isBoolean) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`[populate-all] config "${key}" of type ${typeof value} is not valid. Supported is boolean.`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
case "relations": {
|
|
20
|
+
const isBoolean = typeof value === "boolean";
|
|
21
|
+
const isArrayOfStrings = Array.isArray(value) && value?.every((relation) => typeof relation === "string");
|
|
22
|
+
if (!(isBoolean || isArrayOfStrings)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`[populate-all] config "${key}" of type ${typeof value} is not valid. Supported are boolean or Array of strings.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
default:
|
|
30
|
+
strapi.log.warn(
|
|
31
|
+
`[populate-all] unknown config "${key}" provided. This config will be ignored.`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
2
37
|
const queryCache = {};
|
|
38
|
+
const buildCacheKey = (modelUid, parentsModelUids) => {
|
|
39
|
+
if (parentsModelUids.length === 0) {
|
|
40
|
+
return modelUid;
|
|
41
|
+
}
|
|
42
|
+
return `${parentsModelUids.join("|")}|${modelUid}`;
|
|
43
|
+
};
|
|
3
44
|
const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
4
45
|
try {
|
|
5
46
|
const useCache = strapi.plugin("populate-all").config("cache") ?? true;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
47
|
+
const cacheKey = buildCacheKey(modelUid, parentsModelUids);
|
|
48
|
+
if (useCache && queryCache[cacheKey]) {
|
|
49
|
+
strapi.log.debug(`[populate-all] query cache hit: ${cacheKey}`);
|
|
50
|
+
return structuredClone(queryCache[cacheKey]);
|
|
9
51
|
}
|
|
10
52
|
if (parentsModelUids.includes(modelUid)) {
|
|
11
53
|
strapi.log.debug(
|
|
12
54
|
`[populate-all] loop detected skipping population: ${modelUid}`
|
|
13
55
|
);
|
|
14
56
|
return { populate: {} };
|
|
15
|
-
} else {
|
|
16
|
-
parentsModelUids.push(modelUid);
|
|
17
57
|
}
|
|
58
|
+
const nextParentsModelUids = [...parentsModelUids, modelUid];
|
|
18
59
|
const query = { populate: {} };
|
|
19
60
|
const model = strapi.getModel(modelUid);
|
|
20
61
|
for (const [fieldName, attribute] of Object.entries(
|
|
@@ -28,7 +69,7 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
28
69
|
const components = Object.fromEntries(
|
|
29
70
|
attribute.components.map((component) => [
|
|
30
71
|
component,
|
|
31
|
-
getPopulateQuery(component,
|
|
72
|
+
getPopulateQuery(component, nextParentsModelUids)
|
|
32
73
|
])
|
|
33
74
|
);
|
|
34
75
|
query.populate[fieldName] = { on: components };
|
|
@@ -37,7 +78,7 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
37
78
|
if (attribute.type === "component") {
|
|
38
79
|
query.populate[fieldName] = getPopulateQuery(
|
|
39
80
|
attribute.component,
|
|
40
|
-
|
|
81
|
+
nextParentsModelUids
|
|
41
82
|
);
|
|
42
83
|
continue;
|
|
43
84
|
}
|
|
@@ -50,7 +91,7 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
50
91
|
query.populate[fieldName] = getPopulateQuery(
|
|
51
92
|
// @ts-expect-error target actually exists on attribute
|
|
52
93
|
attribute.target,
|
|
53
|
-
|
|
94
|
+
nextParentsModelUids
|
|
54
95
|
);
|
|
55
96
|
continue;
|
|
56
97
|
}
|
|
@@ -58,7 +99,7 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
58
99
|
query.populate[fieldName] = getPopulateQuery(
|
|
59
100
|
// @ts-expect-error target actually exists on attribute
|
|
60
101
|
attribute.target,
|
|
61
|
-
|
|
102
|
+
nextParentsModelUids
|
|
62
103
|
);
|
|
63
104
|
continue;
|
|
64
105
|
}
|
|
@@ -71,8 +112,8 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
71
112
|
query.populate = true;
|
|
72
113
|
}
|
|
73
114
|
if (useCache) {
|
|
74
|
-
strapi.log.debug(`[populate-all] new query cached: ${
|
|
75
|
-
queryCache[
|
|
115
|
+
strapi.log.debug(`[populate-all] new query cached: ${cacheKey}`);
|
|
116
|
+
queryCache[cacheKey] = query;
|
|
76
117
|
}
|
|
77
118
|
return structuredClone(query);
|
|
78
119
|
} catch (error) {
|
|
@@ -86,7 +127,7 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
86
127
|
strapi2.db.lifecycles.subscribe((event) => {
|
|
87
128
|
try {
|
|
88
129
|
if (event.action === "beforeFindMany" || event.action === "beforeFindOne") {
|
|
89
|
-
if (
|
|
130
|
+
if (event.params._q?.includes(PLUGIN_QUERY_DOCUMENT_TAG)) {
|
|
90
131
|
strapi2.log.debug(
|
|
91
132
|
`[populate-all] recursively populate ${event.model.uid}`
|
|
92
133
|
);
|
|
@@ -94,6 +135,12 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
94
135
|
if (populateQuery?.populate) {
|
|
95
136
|
event.params.populate = populateQuery.populate;
|
|
96
137
|
}
|
|
138
|
+
const query = event.params._q.replace(PLUGIN_QUERY_DOCUMENT_TAG, "");
|
|
139
|
+
if (query.length > 0) {
|
|
140
|
+
event.params._q = query;
|
|
141
|
+
} else {
|
|
142
|
+
delete event.params._q;
|
|
143
|
+
}
|
|
97
144
|
}
|
|
98
145
|
}
|
|
99
146
|
} catch (error) {
|
|
@@ -103,40 +150,6 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
103
150
|
}
|
|
104
151
|
});
|
|
105
152
|
};
|
|
106
|
-
const config = {
|
|
107
|
-
default: {
|
|
108
|
-
relations: true
|
|
109
|
-
},
|
|
110
|
-
validator(config2 = {}) {
|
|
111
|
-
for (const [key, value] of Object.entries(config2)) {
|
|
112
|
-
switch (key) {
|
|
113
|
-
case "cache": {
|
|
114
|
-
const isBoolean = typeof value === "boolean";
|
|
115
|
-
if (!isBoolean) {
|
|
116
|
-
throw new Error(
|
|
117
|
-
`[populate-all] config "${key}" of type ${typeof value} is not valid. Supported is boolean.`
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
case "relations": {
|
|
123
|
-
const isBoolean = typeof value === "boolean";
|
|
124
|
-
const isArrayOfStrings = Array.isArray(value) && value?.every((relation) => typeof relation === "string");
|
|
125
|
-
if (!(isBoolean || isArrayOfStrings)) {
|
|
126
|
-
throw new Error(
|
|
127
|
-
`[populate-all] config "${key}" of type ${typeof value} is not valid. Supported are boolean or Array of strings.`
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
default:
|
|
133
|
-
strapi.log.warn(
|
|
134
|
-
`[populate-all] unknown config "${key}" provided. This config will be ignored.`
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
};
|
|
140
153
|
const middlewares = {
|
|
141
154
|
/**
|
|
142
155
|
* This is a global middleware to add support for the custom query param `?populate=all`.
|
|
@@ -145,9 +158,12 @@ const middlewares = {
|
|
|
145
158
|
* The bootstrap script later picks up `?populateAll=true` to apply the desired populate logic.
|
|
146
159
|
*/
|
|
147
160
|
populateAll: async (ctx, next) => {
|
|
161
|
+
if (ctx.query.populateAll && ctx.query.populateAll !== "false" || ctx.query.populateAll === null) {
|
|
162
|
+
ctx.query._q = [ctx.query._q || "", PLUGIN_QUERY_DOCUMENT_TAG].join("");
|
|
163
|
+
}
|
|
148
164
|
if (ctx.query.populate === "all") {
|
|
149
165
|
ctx.query.populate = void 0;
|
|
150
|
-
ctx.query.
|
|
166
|
+
ctx.query._q = [ctx.query._q || "", PLUGIN_QUERY_DOCUMENT_TAG].join("");
|
|
151
167
|
}
|
|
152
168
|
await next();
|
|
153
169
|
}
|
package/dist/server/index.mjs
CHANGED
|
@@ -1,19 +1,60 @@
|
|
|
1
|
+
const PLUGIN_QUERY_DOCUMENT_TAG = "populate-all";
|
|
2
|
+
const config = {
|
|
3
|
+
default: {
|
|
4
|
+
relations: true
|
|
5
|
+
},
|
|
6
|
+
validator(config2 = {}) {
|
|
7
|
+
for (const [key, value] of Object.entries(config2)) {
|
|
8
|
+
switch (key) {
|
|
9
|
+
case "cache": {
|
|
10
|
+
const isBoolean = typeof value === "boolean";
|
|
11
|
+
if (!isBoolean) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`[populate-all] config "${key}" of type ${typeof value} is not valid. Supported is boolean.`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
case "relations": {
|
|
19
|
+
const isBoolean = typeof value === "boolean";
|
|
20
|
+
const isArrayOfStrings = Array.isArray(value) && value?.every((relation) => typeof relation === "string");
|
|
21
|
+
if (!(isBoolean || isArrayOfStrings)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`[populate-all] config "${key}" of type ${typeof value} is not valid. Supported are boolean or Array of strings.`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
default:
|
|
29
|
+
strapi.log.warn(
|
|
30
|
+
`[populate-all] unknown config "${key}" provided. This config will be ignored.`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
1
36
|
const queryCache = {};
|
|
37
|
+
const buildCacheKey = (modelUid, parentsModelUids) => {
|
|
38
|
+
if (parentsModelUids.length === 0) {
|
|
39
|
+
return modelUid;
|
|
40
|
+
}
|
|
41
|
+
return `${parentsModelUids.join("|")}|${modelUid}`;
|
|
42
|
+
};
|
|
2
43
|
const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
3
44
|
try {
|
|
4
45
|
const useCache = strapi.plugin("populate-all").config("cache") ?? true;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
46
|
+
const cacheKey = buildCacheKey(modelUid, parentsModelUids);
|
|
47
|
+
if (useCache && queryCache[cacheKey]) {
|
|
48
|
+
strapi.log.debug(`[populate-all] query cache hit: ${cacheKey}`);
|
|
49
|
+
return structuredClone(queryCache[cacheKey]);
|
|
8
50
|
}
|
|
9
51
|
if (parentsModelUids.includes(modelUid)) {
|
|
10
52
|
strapi.log.debug(
|
|
11
53
|
`[populate-all] loop detected skipping population: ${modelUid}`
|
|
12
54
|
);
|
|
13
55
|
return { populate: {} };
|
|
14
|
-
} else {
|
|
15
|
-
parentsModelUids.push(modelUid);
|
|
16
56
|
}
|
|
57
|
+
const nextParentsModelUids = [...parentsModelUids, modelUid];
|
|
17
58
|
const query = { populate: {} };
|
|
18
59
|
const model = strapi.getModel(modelUid);
|
|
19
60
|
for (const [fieldName, attribute] of Object.entries(
|
|
@@ -27,7 +68,7 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
27
68
|
const components = Object.fromEntries(
|
|
28
69
|
attribute.components.map((component) => [
|
|
29
70
|
component,
|
|
30
|
-
getPopulateQuery(component,
|
|
71
|
+
getPopulateQuery(component, nextParentsModelUids)
|
|
31
72
|
])
|
|
32
73
|
);
|
|
33
74
|
query.populate[fieldName] = { on: components };
|
|
@@ -36,7 +77,7 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
36
77
|
if (attribute.type === "component") {
|
|
37
78
|
query.populate[fieldName] = getPopulateQuery(
|
|
38
79
|
attribute.component,
|
|
39
|
-
|
|
80
|
+
nextParentsModelUids
|
|
40
81
|
);
|
|
41
82
|
continue;
|
|
42
83
|
}
|
|
@@ -49,7 +90,7 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
49
90
|
query.populate[fieldName] = getPopulateQuery(
|
|
50
91
|
// @ts-expect-error target actually exists on attribute
|
|
51
92
|
attribute.target,
|
|
52
|
-
|
|
93
|
+
nextParentsModelUids
|
|
53
94
|
);
|
|
54
95
|
continue;
|
|
55
96
|
}
|
|
@@ -57,7 +98,7 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
57
98
|
query.populate[fieldName] = getPopulateQuery(
|
|
58
99
|
// @ts-expect-error target actually exists on attribute
|
|
59
100
|
attribute.target,
|
|
60
|
-
|
|
101
|
+
nextParentsModelUids
|
|
61
102
|
);
|
|
62
103
|
continue;
|
|
63
104
|
}
|
|
@@ -70,8 +111,8 @@ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
|
|
|
70
111
|
query.populate = true;
|
|
71
112
|
}
|
|
72
113
|
if (useCache) {
|
|
73
|
-
strapi.log.debug(`[populate-all] new query cached: ${
|
|
74
|
-
queryCache[
|
|
114
|
+
strapi.log.debug(`[populate-all] new query cached: ${cacheKey}`);
|
|
115
|
+
queryCache[cacheKey] = query;
|
|
75
116
|
}
|
|
76
117
|
return structuredClone(query);
|
|
77
118
|
} catch (error) {
|
|
@@ -85,7 +126,7 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
85
126
|
strapi2.db.lifecycles.subscribe((event) => {
|
|
86
127
|
try {
|
|
87
128
|
if (event.action === "beforeFindMany" || event.action === "beforeFindOne") {
|
|
88
|
-
if (
|
|
129
|
+
if (event.params._q?.includes(PLUGIN_QUERY_DOCUMENT_TAG)) {
|
|
89
130
|
strapi2.log.debug(
|
|
90
131
|
`[populate-all] recursively populate ${event.model.uid}`
|
|
91
132
|
);
|
|
@@ -93,6 +134,12 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
93
134
|
if (populateQuery?.populate) {
|
|
94
135
|
event.params.populate = populateQuery.populate;
|
|
95
136
|
}
|
|
137
|
+
const query = event.params._q.replace(PLUGIN_QUERY_DOCUMENT_TAG, "");
|
|
138
|
+
if (query.length > 0) {
|
|
139
|
+
event.params._q = query;
|
|
140
|
+
} else {
|
|
141
|
+
delete event.params._q;
|
|
142
|
+
}
|
|
96
143
|
}
|
|
97
144
|
}
|
|
98
145
|
} catch (error) {
|
|
@@ -102,40 +149,6 @@ const bootstrap = ({ strapi: strapi2 }) => {
|
|
|
102
149
|
}
|
|
103
150
|
});
|
|
104
151
|
};
|
|
105
|
-
const config = {
|
|
106
|
-
default: {
|
|
107
|
-
relations: true
|
|
108
|
-
},
|
|
109
|
-
validator(config2 = {}) {
|
|
110
|
-
for (const [key, value] of Object.entries(config2)) {
|
|
111
|
-
switch (key) {
|
|
112
|
-
case "cache": {
|
|
113
|
-
const isBoolean = typeof value === "boolean";
|
|
114
|
-
if (!isBoolean) {
|
|
115
|
-
throw new Error(
|
|
116
|
-
`[populate-all] config "${key}" of type ${typeof value} is not valid. Supported is boolean.`
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
case "relations": {
|
|
122
|
-
const isBoolean = typeof value === "boolean";
|
|
123
|
-
const isArrayOfStrings = Array.isArray(value) && value?.every((relation) => typeof relation === "string");
|
|
124
|
-
if (!(isBoolean || isArrayOfStrings)) {
|
|
125
|
-
throw new Error(
|
|
126
|
-
`[populate-all] config "${key}" of type ${typeof value} is not valid. Supported are boolean or Array of strings.`
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
default:
|
|
132
|
-
strapi.log.warn(
|
|
133
|
-
`[populate-all] unknown config "${key}" provided. This config will be ignored.`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
152
|
const middlewares = {
|
|
140
153
|
/**
|
|
141
154
|
* This is a global middleware to add support for the custom query param `?populate=all`.
|
|
@@ -144,9 +157,12 @@ const middlewares = {
|
|
|
144
157
|
* The bootstrap script later picks up `?populateAll=true` to apply the desired populate logic.
|
|
145
158
|
*/
|
|
146
159
|
populateAll: async (ctx, next) => {
|
|
160
|
+
if (ctx.query.populateAll && ctx.query.populateAll !== "false" || ctx.query.populateAll === null) {
|
|
161
|
+
ctx.query._q = [ctx.query._q || "", PLUGIN_QUERY_DOCUMENT_TAG].join("");
|
|
162
|
+
}
|
|
147
163
|
if (ctx.query.populate === "all") {
|
|
148
164
|
ctx.query.populate = void 0;
|
|
149
|
-
ctx.query.
|
|
165
|
+
ctx.query._q = [ctx.query._q || "", PLUGIN_QUERY_DOCUMENT_TAG].join("");
|
|
150
166
|
}
|
|
151
167
|
await next();
|
|
152
168
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { UID } from "@strapi/strapi";
|
|
2
|
-
export declare const queryCache:
|
|
2
|
+
export declare const queryCache: Record<string, any>;
|
|
3
3
|
export declare const getPopulateQuery: (modelUid: UID.Schema, parentsModelUids?: UID.Schema[]) => {
|
|
4
4
|
populate: object | true;
|
|
5
5
|
} | undefined;
|
package/package.json
CHANGED