strapi-plugin-publish-media-validation 1.1.1 → 1.1.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAO,MAAM,gBAAgB,CAAC;;2BAiHlB;QAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;KAAE;uBAiC3B;QAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;KAAE;qBACzB;QAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;KAAE;;AAE/C,wBAAgD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAO,MAAM,gBAAgB,CAAC;;2BA+GlB;QAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;KAAE;uBAgE3B;QAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;KAAE;qBACzB;QAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;KAAE;;AAE/C,wBAAgD"}
@@ -24,7 +24,6 @@ function resolveValidationError() {
24
24
  * component references.
25
25
  */
26
26
  function buildPopulate(modelUID, strapi, visited = new Set()) {
27
- var _a;
28
27
  if (visited.has(modelUID))
29
28
  return {};
30
29
  const seen = new Set(visited);
@@ -45,14 +44,12 @@ function buildPopulate(modelUID, strapi, visited = new Set()) {
45
44
  populate[key] = { populate: Object.keys(nested).length ? nested : '*' };
46
45
  }
47
46
  else if (a.type === 'dynamiczone') {
48
- // Merge field names from every possible component type so that
49
- // Strapi populates whichever fields exist on each concrete item.
50
- const merged = {};
51
- for (const compUID of ((_a = a.components) !== null && _a !== void 0 ? _a : [])) {
52
- const nested = buildPopulate(compUID, strapi, seen);
53
- Object.assign(merged, nested);
54
- }
55
- populate[key] = { populate: Object.keys(merged).length ? merged : '*' };
47
+ // Strapi v5 does NOT accept a flat merged-field-name object for
48
+ // dynamic zones only '*' or the per-component 'on' syntax are
49
+ // valid. A flat object causes findOne to throw, producing the
50
+ // generic "Internal Server Error" before our ValidationError is
51
+ // ever reached. Use '*' to load all direct fields of every block.
52
+ populate[key] = { populate: '*' };
56
53
  }
57
54
  }
58
55
  return populate;
@@ -106,19 +103,43 @@ const register = ({ strapi }) => {
106
103
  const documentId = (_a = ctx.params) === null || _a === void 0 ? void 0 : _a.documentId;
107
104
  if (!documentId)
108
105
  return next();
109
- const populate = buildPopulate(ctx.uid, strapi);
110
- if (Object.keys(populate).length === 0)
106
+ let missing = [];
107
+ try {
108
+ const populate = buildPopulate(ctx.uid, strapi);
109
+ if (Object.keys(populate).length > 0) {
110
+ const doc = await strapi.documents(ctx.uid).findOne({
111
+ documentId,
112
+ populate,
113
+ });
114
+ missing = collectMissingMedia(doc, ctx.uid, strapi);
115
+ }
116
+ }
117
+ catch (err) {
118
+ // Re-throw only our own validation errors; swallow unexpected plugin
119
+ // errors so a bug here never blocks a legitimate publish.
120
+ if (err instanceof ValidationError)
121
+ throw err;
122
+ strapi.log.warn(`[publish-media-validation] Skipping validation due to unexpected error: ${err}`);
111
123
  return next();
112
- const doc = await strapi.documents(ctx.uid).findOne({
113
- documentId,
114
- populate,
115
- });
116
- const missing = collectMissingMedia(doc, ctx.uid, strapi);
124
+ }
117
125
  if (missing.length > 0) {
118
126
  const labels = missing
119
- .map((f) => f.charAt(0).toUpperCase() + f.slice(1).replace(/([A-Z])/g, ' $1').replace(/_/g, ' '))
127
+ .map((rawPath) => rawPath
128
+ .split(' > ')
129
+ .map((seg) => {
130
+ const indexed = seg.match(/^(.+?)\[(\d+)\]$/);
131
+ if (indexed) {
132
+ const name = indexed[1].charAt(0).toUpperCase() +
133
+ indexed[1].slice(1).replace(/([A-Z])/g, ' $1').replace(/_/g, ' ');
134
+ return `${name} (item ${indexed[2]})`;
135
+ }
136
+ return seg.charAt(0).toUpperCase() +
137
+ seg.slice(1).replace(/([A-Z])/g, ' $1').replace(/_/g, ' ');
138
+ })
139
+ .join(' › '))
120
140
  .join(', ');
121
- throw new ValidationError(`The following required media fields are empty: ${labels}`);
141
+ const verb = missing.length === 1 ? 'field is' : 'fields are';
142
+ throw new ValidationError(`Cannot publish: required media ${verb} missing — ${labels}`);
122
143
  }
123
144
  return next();
124
145
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-publish-media-validation",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Strapi v5 plugin that enforces required media fields at publish time — works around the known limitation where required: true on media fields is not validated on publish.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -37,6 +37,7 @@ function buildStrapi(
37
37
  getModel: jest.fn().mockReturnValue(
38
38
  modelAttributes !== null ? { attributes: modelAttributes } : null
39
39
  ),
40
+ log: { warn: jest.fn() },
40
41
  };
41
42
 
42
43
  plugin.register({ strapi: strapi as any });
@@ -76,6 +77,7 @@ function buildStrapiFromModels(
76
77
  const attrs = modelsByUid[uid];
77
78
  return attrs !== undefined ? { attributes: attrs } : null;
78
79
  }),
80
+ log: { warn: jest.fn() },
79
81
  };
80
82
 
81
83
  plugin.register({ strapi: strapi as any });
@@ -165,7 +167,7 @@ describe('middleware — flat media fields', () => {
165
167
  );
166
168
  const { result } = runMiddleware(baseCtx);
167
169
  await expect(result).rejects.toThrow(errors.ValidationError);
168
- await expect(result).rejects.toThrow('Cover');
170
+ await expect(result).rejects.toThrow('Cannot publish: required media field is missing — Cover');
169
171
  });
170
172
 
171
173
  it('lists all missing fields in the error message', async () => {
@@ -177,6 +179,7 @@ describe('middleware — flat media fields', () => {
177
179
  { cover: null, heroImage: null }
178
180
  );
179
181
  const { result } = runMiddleware(baseCtx);
182
+ await expect(result).rejects.toThrow('Cannot publish: required media fields are missing');
180
183
  await expect(result).rejects.toThrow('Cover');
181
184
  await expect(result).rejects.toThrow('Hero Image');
182
185
  });
@@ -247,7 +250,7 @@ describe('middleware — component fields', () => {
247
250
  );
248
251
  const { result } = runMiddleware();
249
252
  await expect(result).rejects.toThrow(errors.ValidationError);
250
- await expect(result).rejects.toThrow('Hero > photo');
253
+ await expect(result).rejects.toThrow('Hero Photo');
251
254
  });
252
255
 
253
256
  it('passes through when all repeatable component items have required media', async () => {
@@ -272,7 +275,7 @@ describe('middleware — component fields', () => {
272
275
  { cards: [{ photo: { id: 1 } }, { photo: null }] }
273
276
  );
274
277
  const { result } = runMiddleware();
275
- await expect(result).rejects.toThrow('Cards[2] > photo');
278
+ await expect(result).rejects.toThrow('Cards (item 2) Photo');
276
279
  });
277
280
 
278
281
  it('builds populate that includes the component media field', async () => {
@@ -332,7 +335,7 @@ describe('middleware — dynamiczone fields', () => {
332
335
  );
333
336
  const { result } = runMiddleware();
334
337
  await expect(result).rejects.toThrow(errors.ValidationError);
335
- await expect(result).rejects.toThrow('Blocks[1] > photo');
338
+ await expect(result).rejects.toThrow('Blocks (item 1) Photo');
336
339
  });
337
340
 
338
341
  it('reports the correct block index in a multi-block dynamic zone', async () => {
@@ -349,7 +352,7 @@ describe('middleware — dynamiczone fields', () => {
349
352
  }
350
353
  );
351
354
  const { result } = runMiddleware();
352
- await expect(result).rejects.toThrow('Blocks[2] > photo');
355
+ await expect(result).rejects.toThrow('Blocks (item 2) Photo');
353
356
  });
354
357
 
355
358
  it('skips blocks whose component type has no required media', async () => {
@@ -386,10 +389,10 @@ describe('middleware — dynamiczone fields', () => {
386
389
  }
387
390
  );
388
391
  const { result } = runMiddleware();
389
- await expect(result).rejects.toThrow('Blocks[2] > photo');
392
+ await expect(result).rejects.toThrow('Blocks (item 2) Photo');
390
393
  });
391
394
 
392
- it('builds populate that merges media fields from all component types', async () => {
395
+ it('builds populate using * for dynamic zone fields', async () => {
393
396
  const findOne = jest.fn().mockResolvedValue({
394
397
  blocks: [{ __component: COPY_PHOTO_UID, photo: { id: 1 } }],
395
398
  });
@@ -406,14 +409,17 @@ describe('middleware — dynamiczone fields', () => {
406
409
  if (uid === TEXT_UID) return { attributes: { content: { type: 'richtext' } } };
407
410
  return null;
408
411
  }),
412
+ log: { warn: jest.fn() },
409
413
  };
410
414
 
411
415
  plugin.register({ strapi: strapi as any });
412
416
  await capturedMiddleware!(baseCtx, jest.fn().mockResolvedValue(undefined));
413
417
 
418
+ // Dynamic zones must use populate:'*' — Strapi v5 rejects flat merged
419
+ // field-name objects for dynamic zones, which would cause findOne to throw.
414
420
  expect(findOne).toHaveBeenCalledWith({
415
421
  documentId: 'abc123',
416
- populate: { blocks: { populate: { photo: true } } },
422
+ populate: { blocks: { populate: '*' } },
417
423
  });
418
424
  });
419
425
  });
@@ -50,14 +50,12 @@ function buildPopulate(
50
50
  // to populate:'*' when there are no explicitly known nested keys.
51
51
  populate[key] = { populate: Object.keys(nested).length ? nested : '*' };
52
52
  } else if (a.type === 'dynamiczone') {
53
- // Merge field names from every possible component type so that
54
- // Strapi populates whichever fields exist on each concrete item.
55
- const merged: Record<string, any> = {};
56
- for (const compUID of (a.components ?? [])) {
57
- const nested = buildPopulate(compUID, strapi, seen);
58
- Object.assign(merged, nested);
59
- }
60
- populate[key] = { populate: Object.keys(merged).length ? merged : '*' };
53
+ // Strapi v5 does NOT accept a flat merged-field-name object for
54
+ // dynamic zones only '*' or the per-component 'on' syntax are
55
+ // valid. A flat object causes findOne to throw, producing the
56
+ // generic "Internal Server Error" before our ValidationError is
57
+ // ever reached. Use '*' to load all direct fields of every block.
58
+ populate[key] = { populate: '*' };
61
59
  }
62
60
  }
63
61
 
@@ -123,21 +121,52 @@ const register = ({ strapi }: { strapi: Core.Strapi }) => {
123
121
  const documentId = (ctx.params as { documentId?: string })?.documentId;
124
122
  if (!documentId) return next();
125
123
 
126
- const populate = buildPopulate(ctx.uid as string, strapi);
127
- if (Object.keys(populate).length === 0) return next();
124
+ let missing: string[] = [];
128
125
 
129
- const doc = await strapi.documents(ctx.uid as UID.ContentType).findOne({
130
- documentId,
131
- populate,
132
- });
126
+ try {
127
+ const populate = buildPopulate(ctx.uid as string, strapi);
133
128
 
134
- const missing = collectMissingMedia(doc, ctx.uid as string, strapi);
129
+ if (Object.keys(populate).length > 0) {
130
+ const doc = await strapi.documents(ctx.uid as UID.ContentType).findOne({
131
+ documentId,
132
+ populate,
133
+ });
134
+
135
+ missing = collectMissingMedia(doc, ctx.uid as string, strapi);
136
+ }
137
+ } catch (err) {
138
+ // Re-throw only our own validation errors; swallow unexpected plugin
139
+ // errors so a bug here never blocks a legitimate publish.
140
+ if (err instanceof ValidationError) throw err;
141
+ strapi.log.warn(
142
+ `[publish-media-validation] Skipping validation due to unexpected error: ${err}`
143
+ );
144
+ return next();
145
+ }
135
146
 
136
147
  if (missing.length > 0) {
137
148
  const labels = missing
138
- .map((f) => f.charAt(0).toUpperCase() + f.slice(1).replace(/([A-Z])/g, ' $1').replace(/_/g, ' '))
149
+ .map((rawPath) =>
150
+ rawPath
151
+ .split(' > ')
152
+ .map((seg) => {
153
+ const indexed = seg.match(/^(.+?)\[(\d+)\]$/);
154
+ if (indexed) {
155
+ const name = indexed[1].charAt(0).toUpperCase() +
156
+ indexed[1].slice(1).replace(/([A-Z])/g, ' $1').replace(/_/g, ' ');
157
+ return `${name} (item ${indexed[2]})`;
158
+ }
159
+ return seg.charAt(0).toUpperCase() +
160
+ seg.slice(1).replace(/([A-Z])/g, ' $1').replace(/_/g, ' ');
161
+ })
162
+ .join(' › ')
163
+ )
139
164
  .join(', ');
140
- throw new ValidationError(`The following required media fields are empty: ${labels}`);
165
+
166
+ const verb = missing.length === 1 ? 'field is' : 'fields are';
167
+ throw new ValidationError(
168
+ `Cannot publish: required media ${verb} missing — ${labels}`
169
+ );
141
170
  }
142
171
 
143
172
  return next();