strapi-plugin-populate-all 1.2.1 → 1.2.2

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.
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ const queryCache = {};
3
+ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
4
+ try {
5
+ if (queryCache[modelUid]) {
6
+ strapi.log.debug(`[populate-all] query cache hit: ${modelUid}`);
7
+ return queryCache[modelUid];
8
+ }
9
+ if (parentsModelUids.includes(modelUid)) {
10
+ strapi.log.debug(
11
+ `[populate-all] loop detected skipping population: ${modelUid}`
12
+ );
13
+ return { populate: {} };
14
+ } else {
15
+ parentsModelUids.push(modelUid);
16
+ }
17
+ const query = { populate: {} };
18
+ const model = strapi.getModel(modelUid);
19
+ for (const [fieldName, attribute] of Object.entries(
20
+ model.attributes || {}
21
+ )) {
22
+ if (fieldName === "localizations") {
23
+ query.populate[fieldName] = true;
24
+ continue;
25
+ }
26
+ if (attribute.type === "dynamiczone") {
27
+ const components = Object.fromEntries(
28
+ attribute.components.map((component) => [
29
+ component,
30
+ getPopulateQuery(component, parentsModelUids)
31
+ ])
32
+ );
33
+ query.populate[fieldName] = { on: components };
34
+ continue;
35
+ }
36
+ if (attribute.type === "component") {
37
+ query.populate[fieldName] = getPopulateQuery(
38
+ attribute.component,
39
+ parentsModelUids
40
+ );
41
+ continue;
42
+ }
43
+ if (attribute.type === "relation") {
44
+ if (attribute.private === true) {
45
+ continue;
46
+ }
47
+ const relations = strapi.plugin("populate-all").config("relations");
48
+ if (relations === true) {
49
+ query.populate[fieldName] = getPopulateQuery(
50
+ // @ts-expect-error target actually exists on attribute
51
+ attribute.target,
52
+ parentsModelUids
53
+ );
54
+ continue;
55
+ }
56
+ if (Array.isArray(relations) && relations.includes(attribute.target)) {
57
+ query.populate[fieldName] = getPopulateQuery(
58
+ // @ts-expect-error target actually exists on attribute
59
+ attribute.target,
60
+ parentsModelUids
61
+ );
62
+ continue;
63
+ }
64
+ }
65
+ if (attribute.type === "media") {
66
+ query.populate[fieldName] = true;
67
+ }
68
+ }
69
+ if (Object.keys(query.populate).length === 0) {
70
+ query.populate = true;
71
+ }
72
+ strapi.log.debug(`[populate-all] new query cached: ${modelUid}`);
73
+ queryCache[modelUid] = query;
74
+ return query;
75
+ } catch (error) {
76
+ console.error(
77
+ `[populate-all] getPopulateQuery(${modelUid}) failed: ${error}`
78
+ );
79
+ return void 0;
80
+ }
81
+ };
82
+ const bootstrap = ({ strapi: strapi2 }) => {
83
+ strapi2.db.lifecycles.subscribe((event) => {
84
+ try {
85
+ if (event.params?.recursive === "true") {
86
+ if (event.action === "beforeFindMany" || event.action === "beforeFindOne") {
87
+ strapi2.log.debug(
88
+ `[populate-all] recursively populate ${event.model.uid}`
89
+ );
90
+ const populateQuery = getPopulateQuery(event.model.uid);
91
+ if (populateQuery?.populate) {
92
+ event.params.populate = populateQuery.populate;
93
+ }
94
+ }
95
+ }
96
+ } catch (error) {
97
+ console.error(
98
+ `[populate-all] failed to apply populate db query: ${error}`
99
+ );
100
+ }
101
+ });
102
+ };
103
+ const config = {
104
+ default: {
105
+ relations: true
106
+ },
107
+ validator(config2 = {}) {
108
+ for (const [key, value] of Object.entries(config2)) {
109
+ switch (key) {
110
+ case "relations": {
111
+ const isBoolean = typeof value === "boolean";
112
+ const isArrayOfStrings = Array.isArray(value) && value?.every((relation) => typeof relation === "string");
113
+ if (!(isBoolean || isArrayOfStrings)) {
114
+ throw new Error(
115
+ `[populate-all] config "${key}" of type ${typeof value} is not valid. Supported are boolean or Array of strings.`
116
+ );
117
+ }
118
+ break;
119
+ }
120
+ default:
121
+ strapi.log.warn(
122
+ `[populate-all] unknown config "${key}" provided. This config will be ignored.`
123
+ );
124
+ }
125
+ }
126
+ }
127
+ };
128
+ const middlewares = {
129
+ /**
130
+ * This is a global middleware to add support for the custom query param `?populate=all`.
131
+ * Since Strapi's validator does not allow custom values for the populate param, we intercept the request here.
132
+ * If `?populate=all` is detected, we omit the value and set `?recursive=true` instead.
133
+ * The bootstrap script later picks up `?recursive=true` to apply the desired populate logic.
134
+ */
135
+ populateAll: async (ctx, next) => {
136
+ if (ctx.query.populate === "all") {
137
+ ctx.query.populate = void 0;
138
+ ctx.query.recursive = "true";
139
+ }
140
+ await next();
141
+ }
142
+ };
143
+ const register = ({ strapi: strapi2 }) => {
144
+ strapi2.server.use(middlewares.populateAll);
145
+ };
146
+ const index = {
147
+ config,
148
+ register,
149
+ bootstrap
150
+ };
151
+ module.exports = index;
@@ -0,0 +1,152 @@
1
+ const queryCache = {};
2
+ const getPopulateQuery = (modelUid, parentsModelUids = []) => {
3
+ try {
4
+ if (queryCache[modelUid]) {
5
+ strapi.log.debug(`[populate-all] query cache hit: ${modelUid}`);
6
+ return queryCache[modelUid];
7
+ }
8
+ if (parentsModelUids.includes(modelUid)) {
9
+ strapi.log.debug(
10
+ `[populate-all] loop detected skipping population: ${modelUid}`
11
+ );
12
+ return { populate: {} };
13
+ } else {
14
+ parentsModelUids.push(modelUid);
15
+ }
16
+ const query = { populate: {} };
17
+ const model = strapi.getModel(modelUid);
18
+ for (const [fieldName, attribute] of Object.entries(
19
+ model.attributes || {}
20
+ )) {
21
+ if (fieldName === "localizations") {
22
+ query.populate[fieldName] = true;
23
+ continue;
24
+ }
25
+ if (attribute.type === "dynamiczone") {
26
+ const components = Object.fromEntries(
27
+ attribute.components.map((component) => [
28
+ component,
29
+ getPopulateQuery(component, parentsModelUids)
30
+ ])
31
+ );
32
+ query.populate[fieldName] = { on: components };
33
+ continue;
34
+ }
35
+ if (attribute.type === "component") {
36
+ query.populate[fieldName] = getPopulateQuery(
37
+ attribute.component,
38
+ parentsModelUids
39
+ );
40
+ continue;
41
+ }
42
+ if (attribute.type === "relation") {
43
+ if (attribute.private === true) {
44
+ continue;
45
+ }
46
+ const relations = strapi.plugin("populate-all").config("relations");
47
+ if (relations === true) {
48
+ query.populate[fieldName] = getPopulateQuery(
49
+ // @ts-expect-error target actually exists on attribute
50
+ attribute.target,
51
+ parentsModelUids
52
+ );
53
+ continue;
54
+ }
55
+ if (Array.isArray(relations) && relations.includes(attribute.target)) {
56
+ query.populate[fieldName] = getPopulateQuery(
57
+ // @ts-expect-error target actually exists on attribute
58
+ attribute.target,
59
+ parentsModelUids
60
+ );
61
+ continue;
62
+ }
63
+ }
64
+ if (attribute.type === "media") {
65
+ query.populate[fieldName] = true;
66
+ }
67
+ }
68
+ if (Object.keys(query.populate).length === 0) {
69
+ query.populate = true;
70
+ }
71
+ strapi.log.debug(`[populate-all] new query cached: ${modelUid}`);
72
+ queryCache[modelUid] = query;
73
+ return query;
74
+ } catch (error) {
75
+ console.error(
76
+ `[populate-all] getPopulateQuery(${modelUid}) failed: ${error}`
77
+ );
78
+ return void 0;
79
+ }
80
+ };
81
+ const bootstrap = ({ strapi: strapi2 }) => {
82
+ strapi2.db.lifecycles.subscribe((event) => {
83
+ try {
84
+ if (event.params?.recursive === "true") {
85
+ if (event.action === "beforeFindMany" || event.action === "beforeFindOne") {
86
+ strapi2.log.debug(
87
+ `[populate-all] recursively populate ${event.model.uid}`
88
+ );
89
+ const populateQuery = getPopulateQuery(event.model.uid);
90
+ if (populateQuery?.populate) {
91
+ event.params.populate = populateQuery.populate;
92
+ }
93
+ }
94
+ }
95
+ } catch (error) {
96
+ console.error(
97
+ `[populate-all] failed to apply populate db query: ${error}`
98
+ );
99
+ }
100
+ });
101
+ };
102
+ const config = {
103
+ default: {
104
+ relations: true
105
+ },
106
+ validator(config2 = {}) {
107
+ for (const [key, value] of Object.entries(config2)) {
108
+ switch (key) {
109
+ case "relations": {
110
+ const isBoolean = typeof value === "boolean";
111
+ const isArrayOfStrings = Array.isArray(value) && value?.every((relation) => typeof relation === "string");
112
+ if (!(isBoolean || isArrayOfStrings)) {
113
+ throw new Error(
114
+ `[populate-all] config "${key}" of type ${typeof value} is not valid. Supported are boolean or Array of strings.`
115
+ );
116
+ }
117
+ break;
118
+ }
119
+ default:
120
+ strapi.log.warn(
121
+ `[populate-all] unknown config "${key}" provided. This config will be ignored.`
122
+ );
123
+ }
124
+ }
125
+ }
126
+ };
127
+ const middlewares = {
128
+ /**
129
+ * This is a global middleware to add support for the custom query param `?populate=all`.
130
+ * Since Strapi's validator does not allow custom values for the populate param, we intercept the request here.
131
+ * If `?populate=all` is detected, we omit the value and set `?recursive=true` instead.
132
+ * The bootstrap script later picks up `?recursive=true` to apply the desired populate logic.
133
+ */
134
+ populateAll: async (ctx, next) => {
135
+ if (ctx.query.populate === "all") {
136
+ ctx.query.populate = void 0;
137
+ ctx.query.recursive = "true";
138
+ }
139
+ await next();
140
+ }
141
+ };
142
+ const register = ({ strapi: strapi2 }) => {
143
+ strapi2.server.use(middlewares.populateAll);
144
+ };
145
+ const index = {
146
+ config,
147
+ register,
148
+ bootstrap
149
+ };
150
+ export {
151
+ index as default
152
+ };
@@ -0,0 +1,5 @@
1
+ import type { Core } from "@strapi/strapi";
2
+ declare const bootstrap: ({ strapi }: {
3
+ strapi: Core.Strapi;
4
+ }) => void;
5
+ export default bootstrap;
@@ -0,0 +1,7 @@
1
+ declare const _default: {
2
+ default: {
3
+ relations: boolean;
4
+ };
5
+ validator(config?: Record<string, unknown>): void;
6
+ };
7
+ export default _default;
@@ -0,0 +1,15 @@
1
+ declare const _default: {
2
+ config: {
3
+ default: {
4
+ relations: boolean;
5
+ };
6
+ validator(config?: Record<string, unknown>): void;
7
+ };
8
+ register: ({ strapi }: {
9
+ strapi: import("@strapi/types/dist/core").Strapi;
10
+ }) => void;
11
+ bootstrap: ({ strapi }: {
12
+ strapi: import("@strapi/types/dist/core").Strapi;
13
+ }) => void;
14
+ };
15
+ export default _default;
@@ -0,0 +1,10 @@
1
+ declare const _default: {
2
+ /**
3
+ * This is a global middleware to add support for the custom query param `?populate=all`.
4
+ * Since Strapi's validator does not allow custom values for the populate param, we intercept the request here.
5
+ * If `?populate=all` is detected, we omit the value and set `?recursive=true` instead.
6
+ * The bootstrap script later picks up `?recursive=true` to apply the desired populate logic.
7
+ */
8
+ populateAll: (ctx: any, next: any) => Promise<void>;
9
+ };
10
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import type { Core } from "@strapi/strapi";
2
+ declare const register: ({ strapi }: {
3
+ strapi: Core.Strapi;
4
+ }) => void;
5
+ export default register;
@@ -0,0 +1,4 @@
1
+ import type { UID } from "@strapi/strapi";
2
+ export declare const getPopulateQuery: (modelUid: UID.Schema, parentsModelUids?: UID.Schema[]) => {
3
+ populate: object | true;
4
+ } | undefined;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.1",
2
+ "version": "1.2.2",
3
3
  "name": "strapi-plugin-populate-all",
4
4
  "description": "A lightweight plugin to recursively populate nested data in RESTful API requests",
5
5
  "keywords": [],