strapi-plugin-bulk-updater 1.2.13 → 1.3.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 +10 -4
- package/dist/_chunks/{BulkUpdater-CWJYmuGU.js → BulkUpdater-CRvwX326.js} +261 -33
- package/dist/_chunks/BulkUpdater-CRvwX326.js.map +1 -0
- package/dist/_chunks/{BulkUpdater-Cfig2f7d.mjs → BulkUpdater-DowSmA56.mjs} +261 -33
- package/dist/_chunks/BulkUpdater-DowSmA56.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +250 -125
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +250 -125
- package/dist/server/index.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/_chunks/BulkUpdater-CWJYmuGU.js.map +0 -1
- package/dist/_chunks/BulkUpdater-Cfig2f7d.mjs.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -315,12 +315,13 @@ const bulkUpdaterController = ({ strapi: strapi2 }) => ({
|
|
|
315
315
|
async getCollectionEntries(ctx) {
|
|
316
316
|
try {
|
|
317
317
|
const { uid } = ctx.params;
|
|
318
|
-
const { page, pageSize, attribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
|
|
318
|
+
const { page, pageSize, attribute, searchAttribute, filterOperator, filterQuery, search, sortField, sortOrder } = ctx.query;
|
|
319
319
|
const service = strapi2.plugin("bulk-updater").service("bulkUpdater");
|
|
320
320
|
const data = await service.getCollectionEntries(uid, {
|
|
321
321
|
page: page ? Number(page) : 1,
|
|
322
322
|
pageSize: pageSize ? Number(pageSize) : 10,
|
|
323
323
|
attribute: attribute ? String(attribute) : void 0,
|
|
324
|
+
searchAttribute: searchAttribute ? String(searchAttribute) : void 0,
|
|
324
325
|
filterOperator: filterOperator ? String(filterOperator) : void 0,
|
|
325
326
|
filterQuery: filterQuery !== void 0 ? String(filterQuery) : void 0,
|
|
326
327
|
search: search ? String(search) : void 0,
|
|
@@ -679,6 +680,132 @@ const modelsCache = /* @__PURE__ */ new Map();
|
|
|
679
680
|
const MAX_QUERY_CACHE_SIZE = 500;
|
|
680
681
|
const CACHE_TTL_MS = 60 * 1e3;
|
|
681
682
|
let staticSchemaCache = null;
|
|
683
|
+
function buildDeepPopulate$1(attributes, allComponents, depth2 = 0) {
|
|
684
|
+
if (depth2 > 5) return void 0;
|
|
685
|
+
const populate2 = {};
|
|
686
|
+
for (const [key, def] of Object.entries(attributes || {})) {
|
|
687
|
+
if (def.type === "component" && def.component) {
|
|
688
|
+
const comp = allComponents[def.component];
|
|
689
|
+
if (comp?.attributes) {
|
|
690
|
+
const subPopulate = buildDeepPopulate$1(comp.attributes, allComponents, depth2 + 1);
|
|
691
|
+
if (subPopulate && Object.keys(subPopulate).length > 0) {
|
|
692
|
+
populate2[key] = {
|
|
693
|
+
populate: subPopulate
|
|
694
|
+
};
|
|
695
|
+
} else {
|
|
696
|
+
populate2[key] = true;
|
|
697
|
+
}
|
|
698
|
+
} else {
|
|
699
|
+
populate2[key] = true;
|
|
700
|
+
}
|
|
701
|
+
} else if (def.type === "dynamiczone") {
|
|
702
|
+
populate2[key] = true;
|
|
703
|
+
} else if (def.type === "relation" || def.type === "media") {
|
|
704
|
+
populate2[key] = true;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return Object.keys(populate2).length > 0 ? populate2 : void 0;
|
|
708
|
+
}
|
|
709
|
+
function discoverComponentAttributes(strapi2, compUid, prefix, currentDepth, maxDepth, systemFields, allContentTypes, relationsByAttr, allRelationsMeta, attributesMeta) {
|
|
710
|
+
if (currentDepth > maxDepth || !compUid) return [];
|
|
711
|
+
const compSchema = strapi2.components[compUid];
|
|
712
|
+
if (!compSchema?.attributes) return [];
|
|
713
|
+
const discoveredAttrs = [];
|
|
714
|
+
for (const [subKey, subAttr] of Object.entries(compSchema.attributes)) {
|
|
715
|
+
if (systemFields.includes(subKey)) continue;
|
|
716
|
+
if (subAttr.type === "dynamiczone") continue;
|
|
717
|
+
const fullPath = prefix ? `${prefix}.${subKey}` : subKey;
|
|
718
|
+
if (subAttr.type === "component") {
|
|
719
|
+
if (!subAttr.repeatable && subAttr.component) {
|
|
720
|
+
const deeper = discoverComponentAttributes(
|
|
721
|
+
strapi2,
|
|
722
|
+
subAttr.component,
|
|
723
|
+
fullPath,
|
|
724
|
+
currentDepth + 1,
|
|
725
|
+
maxDepth,
|
|
726
|
+
systemFields,
|
|
727
|
+
allContentTypes,
|
|
728
|
+
relationsByAttr,
|
|
729
|
+
allRelationsMeta,
|
|
730
|
+
attributesMeta
|
|
731
|
+
);
|
|
732
|
+
discoveredAttrs.push(...deeper);
|
|
733
|
+
}
|
|
734
|
+
} else if (subAttr.type === "relation") {
|
|
735
|
+
const targetUid = subAttr.target;
|
|
736
|
+
if (targetUid && allContentTypes[targetUid]) {
|
|
737
|
+
const targetInfo = allContentTypes[targetUid]?.info;
|
|
738
|
+
const isMulti = subAttr.relation === "oneToMany" || subAttr.relation === "manyToMany";
|
|
739
|
+
const relMeta = {
|
|
740
|
+
isRelation: true,
|
|
741
|
+
relationType: subAttr.relation,
|
|
742
|
+
targetUid,
|
|
743
|
+
targetDisplayName: targetInfo?.displayName || targetUid,
|
|
744
|
+
isMulti
|
|
745
|
+
};
|
|
746
|
+
relationsByAttr[fullPath] = relMeta;
|
|
747
|
+
allRelationsMeta[fullPath] = relMeta;
|
|
748
|
+
attributesMeta[fullPath] = { depth: currentDepth, type: "relation", component: compUid, isRelation: true };
|
|
749
|
+
discoveredAttrs.push(fullPath);
|
|
750
|
+
}
|
|
751
|
+
} else {
|
|
752
|
+
attributesMeta[fullPath] = { depth: currentDepth, type: subAttr.type, component: compUid };
|
|
753
|
+
discoveredAttrs.push(fullPath);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return discoveredAttrs;
|
|
757
|
+
}
|
|
758
|
+
function isDeepComponentField(contentType, allComponents, attribute) {
|
|
759
|
+
if (!attribute || !attribute.includes(".")) return false;
|
|
760
|
+
const parts = attribute.split(".");
|
|
761
|
+
let currentSchema = contentType;
|
|
762
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
763
|
+
const part = parts[i];
|
|
764
|
+
const attrDef = currentSchema?.attributes?.[part];
|
|
765
|
+
if (!attrDef || attrDef.type !== "component" || !attrDef.component) return false;
|
|
766
|
+
currentSchema = allComponents[attrDef.component];
|
|
767
|
+
if (!currentSchema) return false;
|
|
768
|
+
}
|
|
769
|
+
const leafField = parts[parts.length - 1];
|
|
770
|
+
return Boolean(currentSchema?.attributes?.[leafField]);
|
|
771
|
+
}
|
|
772
|
+
function getLeafAttributeSchema(contentType, allComponents, attribute) {
|
|
773
|
+
if (!attribute) return null;
|
|
774
|
+
if (!attribute.includes(".")) return contentType?.attributes?.[attribute] || null;
|
|
775
|
+
const parts = attribute.split(".");
|
|
776
|
+
let currentSchema = contentType;
|
|
777
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
778
|
+
const part = parts[i];
|
|
779
|
+
const attrDef = currentSchema?.attributes?.[part];
|
|
780
|
+
if (!attrDef || attrDef.type !== "component" || !attrDef.component) return null;
|
|
781
|
+
currentSchema = allComponents[attrDef.component];
|
|
782
|
+
if (!currentSchema) return null;
|
|
783
|
+
}
|
|
784
|
+
return currentSchema?.attributes?.[parts[parts.length - 1]] || null;
|
|
785
|
+
}
|
|
786
|
+
function setDeepProperty(obj, pathParts, value) {
|
|
787
|
+
if (pathParts.length === 0) return value;
|
|
788
|
+
const current = { ...obj || {} };
|
|
789
|
+
const [head, ...tail] = pathParts;
|
|
790
|
+
if (tail.length === 0) {
|
|
791
|
+
current[head] = value;
|
|
792
|
+
} else {
|
|
793
|
+
current[head] = setDeepProperty(current[head] || {}, tail, value);
|
|
794
|
+
}
|
|
795
|
+
return current;
|
|
796
|
+
}
|
|
797
|
+
function getDeepProperty(obj, path2) {
|
|
798
|
+
if (!obj || !path2) return void 0;
|
|
799
|
+
if (!path2.includes(".")) return obj[path2];
|
|
800
|
+
return path2.split(".").reduce((acc, part) => acc && typeof acc === "object" ? acc[part] : void 0, obj);
|
|
801
|
+
}
|
|
802
|
+
function buildDeepFilter(pathParts, condition) {
|
|
803
|
+
if (pathParts.length === 0) return condition;
|
|
804
|
+
const [head, ...tail] = pathParts;
|
|
805
|
+
return {
|
|
806
|
+
[head]: buildDeepFilter(tail, condition)
|
|
807
|
+
};
|
|
808
|
+
}
|
|
682
809
|
function getSafeTimezone(timeZone) {
|
|
683
810
|
if (!timeZone || typeof timeZone !== "string") {
|
|
684
811
|
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
@@ -887,43 +1014,30 @@ async function executeBatchInternal(strapi2, {
|
|
|
887
1014
|
const currentEntry = await getDocumentDeduplicated(target.uid, target.documentId);
|
|
888
1015
|
const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
|
|
889
1016
|
const isNested = attribute.includes(".");
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
if (isNested) {
|
|
894
|
-
const parts = attribute.split(".");
|
|
895
|
-
parentKey = parts[0];
|
|
896
|
-
childKey = parts[1];
|
|
897
|
-
const parentAttrDef = contentType?.attributes?.[parentKey];
|
|
898
|
-
if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
|
|
899
|
-
const compSchema = strapi2.components[parentAttrDef.component];
|
|
900
|
-
if (compSchema?.attributes?.[childKey]) {
|
|
901
|
-
isComponentNested = true;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
}
|
|
1017
|
+
const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
|
|
1018
|
+
const parts = isNested ? attribute.split(".") : [];
|
|
1019
|
+
const rootKey = isNested ? parts[0] : "";
|
|
905
1020
|
const attrDef = contentType?.attributes?.[attribute];
|
|
906
1021
|
const hasAttribute = Boolean(attrDef) || isComponentNested;
|
|
907
1022
|
if (hasAttribute) {
|
|
908
1023
|
if (isComponentNested) {
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const
|
|
1024
|
+
const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
|
|
1025
|
+
const findOneArgs = { documentId: target.documentId };
|
|
1026
|
+
if (populateOption) {
|
|
1027
|
+
findOneArgs.populate = populateOption;
|
|
1028
|
+
}
|
|
1029
|
+
const currentEntryWithPopulate = await strapi2.documents(target.uid).findOne(findOneArgs);
|
|
1030
|
+
const existingRoot = currentEntryWithPopulate?.[rootKey] || {};
|
|
1031
|
+
const currentVal2 = getDeepProperty(existingRoot, parts.slice(1).join("."));
|
|
915
1032
|
let newValue2 = value;
|
|
916
1033
|
if (regexObj && typeof currentVal2 === "string") {
|
|
917
1034
|
newValue2 = currentVal2.replace(regexObj, value);
|
|
918
1035
|
}
|
|
919
|
-
const
|
|
920
|
-
...existingParent,
|
|
921
|
-
[childKey]: newValue2
|
|
922
|
-
};
|
|
1036
|
+
const updatedRoot = setDeepProperty(existingRoot, parts.slice(1), newValue2);
|
|
923
1037
|
await strapi2.documents(target.uid).update({
|
|
924
1038
|
documentId: target.documentId,
|
|
925
1039
|
data: {
|
|
926
|
-
[
|
|
1040
|
+
[rootKey]: updatedRoot
|
|
927
1041
|
}
|
|
928
1042
|
});
|
|
929
1043
|
const isDraftAndPublishComp = Boolean(contentType?.options?.draftAndPublish);
|
|
@@ -1358,12 +1472,14 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1358
1472
|
}
|
|
1359
1473
|
const metaByUid = {};
|
|
1360
1474
|
const allRelationsMeta = {};
|
|
1475
|
+
const allAttributesMeta = {};
|
|
1361
1476
|
for (const uid of targetUids) {
|
|
1362
1477
|
const contentType = allContentTypes[uid];
|
|
1363
1478
|
if (!contentType || contentType.kind !== "collectionType" && contentType.kind !== "singleType") continue;
|
|
1364
1479
|
const isSingleType = contentType.kind === "singleType";
|
|
1365
1480
|
const rawAttrs = contentType.attributes || {};
|
|
1366
1481
|
const relationsByAttr = {};
|
|
1482
|
+
const colAttributesMeta = {};
|
|
1367
1483
|
const nestedComponentAttrs = [];
|
|
1368
1484
|
const validAttrs = Object.keys(rawAttrs).filter((attrKey) => {
|
|
1369
1485
|
if (systemFields.includes(attrKey)) return false;
|
|
@@ -1371,14 +1487,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1371
1487
|
if (rawAttrs[attrKey].type === "component") {
|
|
1372
1488
|
const compDef = rawAttrs[attrKey];
|
|
1373
1489
|
if (!compDef.repeatable && compDef.component) {
|
|
1374
|
-
const
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1490
|
+
const compAttrs = discoverComponentAttributes(
|
|
1491
|
+
strapi2,
|
|
1492
|
+
compDef.component,
|
|
1493
|
+
attrKey,
|
|
1494
|
+
1,
|
|
1495
|
+
5,
|
|
1496
|
+
systemFields,
|
|
1497
|
+
allContentTypes,
|
|
1498
|
+
relationsByAttr,
|
|
1499
|
+
allRelationsMeta,
|
|
1500
|
+
colAttributesMeta
|
|
1501
|
+
);
|
|
1502
|
+
nestedComponentAttrs.push(...compAttrs);
|
|
1382
1503
|
}
|
|
1383
1504
|
return false;
|
|
1384
1505
|
}
|
|
@@ -1397,12 +1518,19 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1397
1518
|
};
|
|
1398
1519
|
relationsByAttr[attrKey] = relMeta;
|
|
1399
1520
|
allRelationsMeta[attrKey] = relMeta;
|
|
1521
|
+
colAttributesMeta[attrKey] = { depth: 0, type: "relation", isRelation: true };
|
|
1400
1522
|
return true;
|
|
1401
1523
|
}
|
|
1524
|
+
colAttributesMeta[attrKey] = { depth: 0, type: rawAttrs[attrKey].type };
|
|
1402
1525
|
return true;
|
|
1403
1526
|
});
|
|
1404
1527
|
const allColAttrs = [...validAttrs, ...nestedComponentAttrs];
|
|
1405
|
-
allColAttrs.forEach((attr) =>
|
|
1528
|
+
allColAttrs.forEach((attr) => {
|
|
1529
|
+
allAttributesSet.add(attr);
|
|
1530
|
+
if (colAttributesMeta[attr]) {
|
|
1531
|
+
allAttributesMeta[attr] = colAttributesMeta[attr];
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1406
1534
|
metaByUid[uid] = {
|
|
1407
1535
|
uid,
|
|
1408
1536
|
kind: contentType.kind,
|
|
@@ -1412,11 +1540,13 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1412
1540
|
pluralName: contentType.info?.pluralName || uid,
|
|
1413
1541
|
displayName: contentType.info?.displayName || uid,
|
|
1414
1542
|
attributes: allColAttrs,
|
|
1543
|
+
attributesMeta: colAttributesMeta,
|
|
1415
1544
|
relations: relationsByAttr
|
|
1416
1545
|
};
|
|
1417
1546
|
}
|
|
1418
1547
|
staticSchemaCache = {
|
|
1419
1548
|
attributes: Array.from(allAttributesSet),
|
|
1549
|
+
attributesMeta: allAttributesMeta,
|
|
1420
1550
|
targetUids: Object.keys(metaByUid),
|
|
1421
1551
|
metaByUid,
|
|
1422
1552
|
relationsMeta: allRelationsMeta
|
|
@@ -1447,32 +1577,27 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1447
1577
|
let entries = [];
|
|
1448
1578
|
let totalCount = 0;
|
|
1449
1579
|
try {
|
|
1450
|
-
const
|
|
1451
|
-
const relKeys = Object.keys(meta.relations || {});
|
|
1452
|
-
const compKeys = Object.keys(ctAttrs).filter((k) => ctAttrs[k].type === "component");
|
|
1453
|
-
const populateOptionObj = {};
|
|
1454
|
-
relKeys.forEach((k) => {
|
|
1455
|
-
populateOptionObj[k] = true;
|
|
1456
|
-
});
|
|
1457
|
-
compKeys.forEach((k) => {
|
|
1458
|
-
populateOptionObj[k] = true;
|
|
1459
|
-
});
|
|
1460
|
-
const populateOption = Object.keys(populateOptionObj).length > 0 ? populateOptionObj : void 0;
|
|
1580
|
+
const populateOption = buildDeepPopulate$1(allContentTypes[uid]?.attributes || {}, strapi2.components);
|
|
1461
1581
|
if (meta.isSingleType) {
|
|
1462
|
-
const
|
|
1463
|
-
|
|
1464
|
-
|
|
1582
|
+
const singleArgs = {};
|
|
1583
|
+
if (populateOption) {
|
|
1584
|
+
singleArgs.populate = populateOption;
|
|
1585
|
+
}
|
|
1586
|
+
const singleDoc = await strapi2.documents(uid).findFirst(singleArgs);
|
|
1465
1587
|
if (singleDoc) {
|
|
1466
1588
|
entries = [singleDoc];
|
|
1467
1589
|
totalCount = 1;
|
|
1468
1590
|
}
|
|
1469
1591
|
} else {
|
|
1470
1592
|
totalCount = await strapi2.documents(uid).count();
|
|
1471
|
-
|
|
1593
|
+
const findArgs = {
|
|
1472
1594
|
limit: 10,
|
|
1473
|
-
sort: { createdAt: "desc" }
|
|
1474
|
-
|
|
1475
|
-
|
|
1595
|
+
sort: { createdAt: "desc" }
|
|
1596
|
+
};
|
|
1597
|
+
if (populateOption) {
|
|
1598
|
+
findArgs.populate = populateOption;
|
|
1599
|
+
}
|
|
1600
|
+
entries = await strapi2.documents(uid).findMany(findArgs);
|
|
1476
1601
|
}
|
|
1477
1602
|
} catch (err) {
|
|
1478
1603
|
strapi2.log.error(`[bulk-updater] Failed to fetch entries for ${uid}:`, err);
|
|
@@ -1516,8 +1641,10 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1516
1641
|
return {
|
|
1517
1642
|
success: true,
|
|
1518
1643
|
attributes: staticSchemaCache.attributes,
|
|
1644
|
+
attributesMeta: staticSchemaCache.attributesMeta,
|
|
1519
1645
|
collections,
|
|
1520
1646
|
relationsMeta: staticSchemaCache.relationsMeta,
|
|
1647
|
+
maxDiscoveredDepth: 5,
|
|
1521
1648
|
executionWindow: {
|
|
1522
1649
|
...executionConfig,
|
|
1523
1650
|
...windowStatus
|
|
@@ -1536,6 +1663,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1536
1663
|
page = 1,
|
|
1537
1664
|
pageSize = 10,
|
|
1538
1665
|
attribute,
|
|
1666
|
+
searchAttribute,
|
|
1539
1667
|
filterOperator,
|
|
1540
1668
|
filterQuery,
|
|
1541
1669
|
search,
|
|
@@ -1550,8 +1678,9 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1550
1678
|
const currentPage = Math.max(Number(page) || 1, 1);
|
|
1551
1679
|
const start = (currentPage - 1) * limit;
|
|
1552
1680
|
const isSingleType = contentType.kind === "singleType";
|
|
1681
|
+
const targetFilterAttr = searchAttribute || attribute;
|
|
1553
1682
|
const signature = await getCollectionSignature(strapi2, uid);
|
|
1554
|
-
const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
|
|
1683
|
+
const cacheKey = `${uid}:${limit}:${start}:${attribute || ""}:${searchAttribute || ""}:${filterOperator || ""}:${filterQuery || ""}:${search || ""}:${sortField || ""}:${sortOrder}`;
|
|
1555
1684
|
const cached = queryCache.get(cacheKey);
|
|
1556
1685
|
const etag = `W/"entry-${crypto.createHash("md5").update(`${cacheKey}:${signature}`).digest("hex")}"`;
|
|
1557
1686
|
if (cached && cached.signature === signature && Date.now() - cached.timestamp < CACHE_TTL_MS) {
|
|
@@ -1566,19 +1695,15 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1566
1695
|
const attrs = contentType.attributes || {};
|
|
1567
1696
|
let colHasAttribute = false;
|
|
1568
1697
|
let isNestedAttr = false;
|
|
1569
|
-
let
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
if (compSchema?.attributes?.[childKey]) {
|
|
1577
|
-
colHasAttribute = true;
|
|
1578
|
-
isNestedAttr = true;
|
|
1579
|
-
}
|
|
1698
|
+
let parts = [];
|
|
1699
|
+
if (targetFilterAttr) {
|
|
1700
|
+
if (targetFilterAttr.includes(".")) {
|
|
1701
|
+
isNestedAttr = isDeepComponentField(contentType, strapi2.components, targetFilterAttr);
|
|
1702
|
+
if (isNestedAttr) {
|
|
1703
|
+
colHasAttribute = true;
|
|
1704
|
+
parts = targetFilterAttr.split(".");
|
|
1580
1705
|
}
|
|
1581
|
-
} else if (attrs[
|
|
1706
|
+
} else if (attrs[targetFilterAttr]) {
|
|
1582
1707
|
colHasAttribute = true;
|
|
1583
1708
|
}
|
|
1584
1709
|
}
|
|
@@ -1603,7 +1728,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1603
1728
|
}
|
|
1604
1729
|
let condition = void 0;
|
|
1605
1730
|
const q = (filterQuery || "").trim();
|
|
1606
|
-
const targetAttrSchema = isNestedAttr ? strapi2.components
|
|
1731
|
+
const targetAttrSchema = isNestedAttr ? getLeafAttributeSchema(contentType, strapi2.components, targetFilterAttr) : attrs[targetFilterAttr];
|
|
1607
1732
|
const isStringLike = ["string", "text", "email", "richtext", "uid"].includes(targetAttrSchema?.type);
|
|
1608
1733
|
if (normOp === "empty") {
|
|
1609
1734
|
condition = isStringLike ? { $or: [{ $null: true }, { $eq: "" }] } : { $null: true };
|
|
@@ -1633,19 +1758,18 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1633
1758
|
}
|
|
1634
1759
|
if (condition !== void 0) {
|
|
1635
1760
|
if (isNestedAttr) {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
};
|
|
1761
|
+
const deepFilter = buildDeepFilter(parts, condition);
|
|
1762
|
+
Object.assign(filters2, deepFilter);
|
|
1639
1763
|
} else {
|
|
1640
1764
|
if (normOp === "empty" && isStringLike) {
|
|
1641
1765
|
filters2.$or = [
|
|
1642
|
-
{ [
|
|
1643
|
-
{ [
|
|
1766
|
+
{ [targetFilterAttr]: { $null: true } },
|
|
1767
|
+
{ [targetFilterAttr]: { $eq: "" } }
|
|
1644
1768
|
];
|
|
1645
1769
|
} else if (normOp === "notempty" && isStringLike) {
|
|
1646
|
-
filters2[
|
|
1770
|
+
filters2[targetFilterAttr] = { $notNull: true, $ne: "" };
|
|
1647
1771
|
} else {
|
|
1648
|
-
filters2[
|
|
1772
|
+
filters2[targetFilterAttr] = condition;
|
|
1649
1773
|
}
|
|
1650
1774
|
}
|
|
1651
1775
|
}
|
|
@@ -1668,12 +1792,17 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1668
1792
|
}
|
|
1669
1793
|
}
|
|
1670
1794
|
}
|
|
1795
|
+
const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
|
|
1671
1796
|
if (isSingleType) {
|
|
1672
1797
|
let singleDoc = null;
|
|
1673
1798
|
try {
|
|
1674
|
-
|
|
1799
|
+
const singleArgs = {
|
|
1675
1800
|
filters: Object.keys(filters2).length > 0 ? filters2 : void 0
|
|
1676
|
-
}
|
|
1801
|
+
};
|
|
1802
|
+
if (populateOption) {
|
|
1803
|
+
singleArgs.populate = populateOption;
|
|
1804
|
+
}
|
|
1805
|
+
singleDoc = await strapi2.documents(uid).findFirst(singleArgs);
|
|
1677
1806
|
} catch {
|
|
1678
1807
|
singleDoc = null;
|
|
1679
1808
|
}
|
|
@@ -1691,11 +1820,11 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1691
1820
|
}
|
|
1692
1821
|
};
|
|
1693
1822
|
} else {
|
|
1694
|
-
|
|
1823
|
+
let sortClause = {};
|
|
1695
1824
|
if (sortField && sortField.includes(".")) {
|
|
1696
|
-
const
|
|
1697
|
-
if (contentType.
|
|
1698
|
-
sortClause
|
|
1825
|
+
const sortParts = sortField.split(".");
|
|
1826
|
+
if (isDeepComponentField(contentType, strapi2.components, sortField)) {
|
|
1827
|
+
sortClause = buildDeepFilter(sortParts, sortOrder);
|
|
1699
1828
|
} else {
|
|
1700
1829
|
sortClause["createdAt"] = "desc";
|
|
1701
1830
|
}
|
|
@@ -1704,18 +1833,17 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1704
1833
|
} else {
|
|
1705
1834
|
sortClause["createdAt"] = "desc";
|
|
1706
1835
|
}
|
|
1707
|
-
const
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1836
|
+
const findManyArgs = {
|
|
1837
|
+
start,
|
|
1838
|
+
limit,
|
|
1839
|
+
filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
|
|
1840
|
+
sort: sortClause
|
|
1841
|
+
};
|
|
1842
|
+
if (populateOption) {
|
|
1843
|
+
findManyArgs.populate = populateOption;
|
|
1844
|
+
}
|
|
1711
1845
|
const [entries, totalEntries] = await Promise.all([
|
|
1712
|
-
strapi2.documents(uid).findMany(
|
|
1713
|
-
start,
|
|
1714
|
-
limit,
|
|
1715
|
-
filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
|
|
1716
|
-
sort: sortClause,
|
|
1717
|
-
populate: populateOption
|
|
1718
|
-
}),
|
|
1846
|
+
strapi2.documents(uid).findMany(findManyArgs),
|
|
1719
1847
|
strapi2.documents(uid).count({
|
|
1720
1848
|
filters: Object.keys(filters2).length > 0 ? filters2 : void 0
|
|
1721
1849
|
})
|
|
@@ -1829,21 +1957,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1829
1957
|
const displayName = contentType?.info?.displayName || target.uid;
|
|
1830
1958
|
try {
|
|
1831
1959
|
const isNested = attribute.includes(".");
|
|
1832
|
-
|
|
1833
|
-
let parentKey = "";
|
|
1834
|
-
let childKey = "";
|
|
1835
|
-
if (isNested) {
|
|
1836
|
-
const parts = attribute.split(".");
|
|
1837
|
-
parentKey = parts[0];
|
|
1838
|
-
childKey = parts[1];
|
|
1839
|
-
const parentAttrDef = contentType?.attributes?.[parentKey];
|
|
1840
|
-
if (parentAttrDef?.type === "component" && !parentAttrDef.repeatable && parentAttrDef.component) {
|
|
1841
|
-
const compSchema = strapi2.components[parentAttrDef.component];
|
|
1842
|
-
if (compSchema?.attributes?.[childKey]) {
|
|
1843
|
-
isComponentNested = true;
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1960
|
+
const isComponentNested = isNested && isDeepComponentField(contentType, strapi2.components, attribute);
|
|
1847
1961
|
const attrDef = contentType?.attributes?.[attribute];
|
|
1848
1962
|
const hasAttribute = Boolean(attrDef) || isComponentNested;
|
|
1849
1963
|
if (!hasAttribute) {
|
|
@@ -1866,10 +1980,12 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1866
1980
|
}
|
|
1867
1981
|
let currentEntry;
|
|
1868
1982
|
if (isComponentNested) {
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1983
|
+
const findOneArgs = { documentId: target.documentId };
|
|
1984
|
+
const populateOption = buildDeepPopulate$1(contentType.attributes, strapi2.components);
|
|
1985
|
+
if (populateOption) {
|
|
1986
|
+
findOneArgs.populate = populateOption;
|
|
1987
|
+
}
|
|
1988
|
+
currentEntry = await strapi2.documents(target.uid).findOne(findOneArgs);
|
|
1873
1989
|
} else if (attrDef?.type === "relation") {
|
|
1874
1990
|
currentEntry = await strapi2.documents(target.uid).findOne({
|
|
1875
1991
|
documentId: target.documentId,
|
|
@@ -1882,8 +1998,7 @@ const bulkUpdaterService = ({ strapi: strapi2 }) => {
|
|
|
1882
1998
|
}
|
|
1883
1999
|
const entryLabel = currentEntry?.name || currentEntry?.title || currentEntry?.email || `Entry #${target.documentId}`;
|
|
1884
2000
|
if (isComponentNested) {
|
|
1885
|
-
const
|
|
1886
|
-
const currentVal2 = existingParent?.[childKey];
|
|
2001
|
+
const currentVal2 = getDeepProperty(currentEntry, attribute);
|
|
1887
2002
|
let newValue2 = value;
|
|
1888
2003
|
if (regexObj && typeof currentVal2 === "string") {
|
|
1889
2004
|
newValue2 = currentVal2.replace(regexObj, value);
|
|
@@ -3619,15 +3734,20 @@ function discoverExportFields(ct, allComponents, maxDepth = 4) {
|
|
|
3619
3734
|
return fields2;
|
|
3620
3735
|
}
|
|
3621
3736
|
function buildDeepPopulate(attributes, allComponents, depth2 = 0) {
|
|
3622
|
-
if (depth2 >
|
|
3737
|
+
if (depth2 > 5) return void 0;
|
|
3623
3738
|
const populate2 = {};
|
|
3624
3739
|
for (const [key, def] of Object.entries(attributes || {})) {
|
|
3625
3740
|
if (def.type === "component" && def.component) {
|
|
3626
3741
|
const comp = allComponents[def.component];
|
|
3627
3742
|
if (comp?.attributes) {
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3743
|
+
const subPopulate = buildDeepPopulate(comp.attributes, allComponents, depth2 + 1);
|
|
3744
|
+
if (subPopulate && Object.keys(subPopulate).length > 0) {
|
|
3745
|
+
populate2[key] = {
|
|
3746
|
+
populate: subPopulate
|
|
3747
|
+
};
|
|
3748
|
+
} else {
|
|
3749
|
+
populate2[key] = true;
|
|
3750
|
+
}
|
|
3631
3751
|
} else {
|
|
3632
3752
|
populate2[key] = true;
|
|
3633
3753
|
}
|
|
@@ -3637,7 +3757,7 @@ function buildDeepPopulate(attributes, allComponents, depth2 = 0) {
|
|
|
3637
3757
|
populate2[key] = true;
|
|
3638
3758
|
}
|
|
3639
3759
|
}
|
|
3640
|
-
return Object.keys(populate2).length > 0 ? populate2 :
|
|
3760
|
+
return Object.keys(populate2).length > 0 ? populate2 : void 0;
|
|
3641
3761
|
}
|
|
3642
3762
|
function getNestedExportValue(entry, path2) {
|
|
3643
3763
|
if (!entry || !path2) return "";
|
|
@@ -3804,17 +3924,22 @@ const csvService = ({ strapi: strapi2 }) => ({
|
|
|
3804
3924
|
const deepPopulate = buildDeepPopulate(ct.attributes || {}, allComponents);
|
|
3805
3925
|
let entries = [];
|
|
3806
3926
|
if (ct.kind === "singleType") {
|
|
3807
|
-
const
|
|
3808
|
-
|
|
3809
|
-
|
|
3927
|
+
const findFirstArgs = {};
|
|
3928
|
+
if (deepPopulate) {
|
|
3929
|
+
findFirstArgs.populate = deepPopulate;
|
|
3930
|
+
}
|
|
3931
|
+
const doc = await strapi2.documents(uid).findFirst(findFirstArgs);
|
|
3810
3932
|
entries = doc ? [doc] : [];
|
|
3811
3933
|
} else {
|
|
3812
|
-
|
|
3934
|
+
const findManyArgs = {
|
|
3813
3935
|
filters: Object.keys(filters2).length > 0 ? filters2 : void 0,
|
|
3814
3936
|
limit: options.limit || 5e3,
|
|
3815
|
-
sort: { createdAt: "desc" }
|
|
3816
|
-
|
|
3817
|
-
|
|
3937
|
+
sort: { createdAt: "desc" }
|
|
3938
|
+
};
|
|
3939
|
+
if (deepPopulate) {
|
|
3940
|
+
findManyArgs.populate = deepPopulate;
|
|
3941
|
+
}
|
|
3942
|
+
entries = await strapi2.documents(uid).findMany(findManyArgs);
|
|
3818
3943
|
}
|
|
3819
3944
|
const lines = [];
|
|
3820
3945
|
lines.push(fields2.map((f) => escapeCsvValue(f.key)).join(","));
|