Skip to content

Commit 07b2844

Browse files
committed
fix(Assets): fix asset tagging after projectfiles move
1 parent 81eac44 commit 07b2844

File tree

6 files changed

+258
-272
lines changed

6 files changed

+258
-272
lines changed

server/api.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1453,7 +1453,7 @@ router.route('/project/:projectID/files/:fileID?')
14531453
).put(
14541454
authAPI.verifyRequest,
14551455
authAPI.getUserAttributes,
1456-
middleware.validateZod(ProjectValidators.updateProjectFileSchema),
1456+
middleware.validateZod(ProjectFileValidators.updateProjectFileSchema),
14571457
projectfilesAPI.fileUploadHandler,
14581458
projectfilesAPI.updateProjectFile,
14591459
).delete(

server/api/assettagging.ts

+31-37
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import AssetTag, { AssetTagInterface } from "../models/assettag.js";
22
import { v4 } from "uuid";
3-
import { ProjectFileInterface } from "../models/projectfile.js";
4-
import FileAssetTags from "../models/fileassettags.js";
3+
import ProjectFile, { ProjectFileInterface } from "../models/projectfile.js";
54
import AssetTagKey, { AssetTagKeyInterface } from "../models/assettagkey.js";
65
import { getRandomColor } from "../util/assettaggingutils.js";
76
import { Types, isObjectIdOrHexString } from "mongoose";
@@ -12,46 +11,42 @@ import {
1211

1312
async function upsertAssetTags(
1413
file: ProjectFileInterface,
15-
tags: AssetTagInterface[]
14+
reqTags: AssetTagInterface[]
1615
): Promise<void> {
1716
try {
18-
const reqTags = tags;
19-
let refDoc = await FileAssetTags.findOne({ fileID: file._id });
20-
let createdRefDoc = false;
21-
22-
if (!refDoc) {
23-
createdRefDoc = true;
24-
refDoc = new FileAssetTags({
25-
fileID: new Types.ObjectId(file._id),
26-
tags: [],
27-
});
28-
}
17+
const existingTagIds = file.tags ?? [];
18+
const existingTags = await AssetTag.find({ _id: { $in: existingTagIds } });
2919

3020
// Map keys to array and then search DB so we only have to do one query
31-
const keysInTags = reqTags.map((t) => t.key);
3221
const existingKeys = await AssetTagKey.find({
3322
orgID: process.env.ORG_ID,
34-
title: { $in: keysInTags },
3523
isDeleted: { $ne: true },
3624
});
3725

38-
const currTags = await AssetTag.find({ _id: { $in: refDoc?.tags } });
3926
const newTags: AssetTagInterface[] = [];
4027

41-
//Find deleted tags where the tag is in the refDoc but not in the tags array (and has a uuid) (presumed it was removed)
28+
//Find deleted tags where the tag is in the existing tags but not in the tags array (and has a uuid) (presumed it was removed)
4229
const reqTagUUIDs = reqTags.map((t) => t.uuid).filter((u) => !!u);
43-
44-
const deletedTags = currTags.filter((t) => t.uuid && !reqTagUUIDs.includes(t.uuid));
45-
for (const tag of deletedTags) {
46-
await tag.deleteOne();
30+
const deletedTags = existingTags.filter(
31+
(t) => t.uuid && !reqTagUUIDs.includes(t.uuid)
32+
);
33+
if (deletedTags.length > 0) {
34+
await AssetTag.deleteMany({
35+
_id: { $in: deletedTags.map((t) => t._id) },
36+
});
4737
}
4838

49-
currTags.filter((t) => !deletedTags.includes(t)); //Remove deleted tags from currTags
39+
// Filter out the deleted tags from the existing tags
40+
const existingWithoutDeleted = existingTags.filter(
41+
(t) => !deletedTags.includes(t)
42+
);
5043
for (const tag of reqTags) {
51-
const existingTag = currTags.find((t) => t._id.equals(tag._id));
44+
const existingTag = existingWithoutDeleted.find((t) =>
45+
t._id.equals(tag._id)
46+
);
5247

53-
// If the tag already exists, update it
5448
if (existingTag) {
49+
// If the tag already exists, update it
5550
existingTag.value = tag.value;
5651
existingTag.framework = tag.framework;
5752
existingTag.key = new Types.ObjectId(
@@ -62,9 +57,8 @@ async function upsertAssetTags(
6257
) // Pass the key from the request in case it was updated and is no longer a valid mongoID/object
6358
);
6459
await existingTag.save();
65-
}
66-
// If the tag is new, create it
67-
else {
60+
} else {
61+
// If the tag is new, create it
6862
tag.key = new Types.ObjectId(
6963
await getUpsertedAssetTagKey(existingKeys, tag)
7064
);
@@ -77,16 +71,16 @@ async function upsertAssetTags(
7771
}
7872
}
7973

80-
const finalTags = [...currTags, ...newTags];
81-
82-
// If there are no tags, and we created a refDoc, don't save it
83-
if (finalTags.length === 0 && createdRefDoc) {
84-
return;
85-
}
74+
const finalTags = [...existingWithoutDeleted, ...newTags];
8675

87-
// Update refDoc
88-
refDoc.tags = finalTags.map((t) => t._id);
89-
await refDoc.save();
76+
await ProjectFile.updateOne(
77+
{
78+
fileID: file.fileID,
79+
},
80+
{
81+
tags: finalTags.map((t) => t._id),
82+
}
83+
);
9084
} catch (err) {
9185
throw err;
9286
}

server/api/projectfiles.ts

+77-88
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { NextFunction, Request, Response } from "express";
22
import conductorErrors from "../conductor-errors.js";
3-
import ProjectFile, { ProjectFileInterface } from "../models/projectfile.js";
3+
import ProjectFile, {
4+
ProjectFileInterface,
5+
RawProjectFileInterface,
6+
} from "../models/projectfile.js";
47
import multer from "multer";
58
import Project from "../models/project.js";
69
import {
@@ -532,13 +535,6 @@ async function updateProjectFile(
532535
) {
533536
try {
534537
const { projectID, fileID } = req.params;
535-
const project = await Project.findOne({ projectID }).lean();
536-
if (!project) {
537-
return res.status(404).send({
538-
err: true,
539-
errMsg: conductorErrors.err11,
540-
});
541-
}
542538

543539
const {
544540
name,
@@ -555,18 +551,14 @@ async function updateProjectFile(
555551
const shouldOverwriteName =
556552
overwriteName === undefined ? true : overwriteName;
557553

558-
const files = await retrieveAllProjectFiles(
554+
const [file] = await retrieveSingleProjectFile(
559555
projectID,
556+
fileID,
560557
false,
561558
req.user.decoded.uuid
562559
);
563-
if (!files) {
564-
// error encountered
565-
throw new Error("retrieveerror");
566-
}
567560

568-
const foundObj = files.find((obj) => obj.fileID === fileID);
569-
if (!foundObj) {
561+
if (!file) {
570562
return res.status(400).send({
571563
err: true,
572564
errMsg: conductorErrors.err63,
@@ -589,7 +581,7 @@ async function updateProjectFile(
589581
if (processedName) {
590582
// Ensure file extension remains in new name
591583
if (!processedName.includes(".")) {
592-
const splitCurrName = foundObj.name?.split(".") ?? [];
584+
const splitCurrName = file.name?.split(".") ?? [];
593585
if (splitCurrName.length > 1) {
594586
const currExtension = splitCurrName[splitCurrName.length - 1];
595587
processedName = `${processedName}.${currExtension}`;
@@ -600,79 +592,73 @@ async function updateProjectFile(
600592
// update tags
601593
if (tags) {
602594
//@ts-ignore
603-
await upsertAssetTags(foundObj, tags);
595+
await upsertAssetTags(file, tags);
604596
}
605597

606-
const updated = files.map((obj) => {
607-
if (obj.fileID === foundObj.fileID) {
608-
const updateObj = { ...obj };
609-
if (processedName) {
610-
updateObj.name = processedName;
611-
}
612-
if (typeof description === "string") {
613-
// account for unsetting
614-
updateObj.description = description;
615-
}
616-
if (license) {
617-
updateObj.license = license;
618-
}
619-
if (authors) {
620-
const parseAuthors = (authorsData: any[]) => {
621-
if (!Array.isArray(authorsData)) return [];
622-
const reduced = authorsData.reduce((acc, curr) => {
623-
if (curr._id) {
624-
acc.push(new Types.ObjectId(curr._id));
625-
} else {
626-
acc.push(curr);
627-
}
628-
return acc;
629-
}, []);
630-
return reduced;
631-
};
598+
const updateObj = {} as RawProjectFileInterface;
599+
if (processedName) {
600+
updateObj.name = processedName;
601+
}
602+
if (typeof description === "string") {
603+
// account for unsetting
604+
updateObj.description = description;
605+
}
606+
if (license) {
607+
updateObj.license = license;
608+
}
609+
if (authors) {
610+
const parseAuthors = (authorsData: any[]) => {
611+
if (!Array.isArray(authorsData)) return [];
612+
const reduced = authorsData.reduce((acc, curr) => {
613+
if (curr._id) {
614+
acc.push(new Types.ObjectId(curr._id));
615+
} else {
616+
acc.push(curr);
617+
}
618+
return acc;
619+
}, []);
620+
return reduced;
621+
};
632622

633-
const parsed = parseAuthors(authors);
623+
const parsed = parseAuthors(authors);
634624

635-
updateObj.authors = parsed;
636-
}
637-
if (publisher) {
638-
updateObj.publisher = publisher;
639-
}
640-
if (req.files && req.files[0]) {
641-
updateObj.version = obj.version ? obj.version + 1 : 1; // increment version
642-
if (req.files[0].mimetype) {
643-
updateObj.mimeType = req.files[0].mimetype; // update mime type
644-
}
645-
if (req.files[0].size) {
646-
updateObj.size = req.files[0].size; // update size
647-
}
648-
}
649-
// allow updating of URL if file is a URL
650-
if (
651-
Boolean(isURL) &&
652-
fileURL
653-
//&& obj.isURL && obj.url !== fileURL
654-
) {
655-
updateObj.isURL = true;
656-
updateObj.url = fileURL;
657-
updateObj.storageType = "file";
658-
updateObj.size = 0;
659-
updateObj.downloadCount = undefined;
660-
updateObj.mimeType = undefined;
661-
updateObj.license = {
662-
...obj.license,
663-
sourceURL: fileURL,
664-
};
665-
}
666-
return updateObj;
625+
updateObj.authors = parsed;
626+
}
627+
if (publisher) {
628+
updateObj.publisher = publisher;
629+
}
630+
if (req.files && req.files[0]) {
631+
updateObj.version = file.version ? file.version + 1 : 1; // increment version
632+
if (req.files[0].mimetype) {
633+
updateObj.mimeType = req.files[0].mimetype; // update mime type
667634
}
668-
return obj;
669-
});
635+
if (req.files[0].size) {
636+
updateObj.size = req.files[0].size; // update size
637+
}
638+
}
639+
// allow updating of URL if file is a URL
640+
if (
641+
Boolean(isURL) &&
642+
fileURL
643+
//&& obj.isURL && obj.url !== fileURL
644+
) {
645+
updateObj.isURL = true;
646+
updateObj.url = fileURL;
647+
updateObj.storageType = "file";
648+
updateObj.size = 0;
649+
updateObj.downloadCount = undefined;
650+
updateObj.mimeType = undefined;
651+
updateObj.license = {
652+
...file.license,
653+
sourceURL: fileURL,
654+
};
655+
}
670656

671657
const storageClient = new S3Client(PROJECT_FILES_S3_CLIENT_CONFIG);
672658

673659
const isPhysicalFile =
674-
foundObj.storageType === "file" && !foundObj.isURL && !foundObj.url;
675-
if (isPhysicalFile && processedName && processedName !== foundObj.name) {
660+
file.storageType === "file" && !file.isURL && !file.url;
661+
if (isPhysicalFile && processedName && processedName !== file.name) {
676662
// rename file
677663
const fileKey = `${projectID}/${fileID}`;
678664
const storageClient = new S3Client(PROJECT_FILES_S3_CLIENT_CONFIG);
@@ -717,7 +703,7 @@ async function updateProjectFile(
717703
}
718704

719705
// Delete the old file if it has been replaced with a URL
720-
if (foundObj.storageType === "file" && isURL && fileURL) {
706+
if (file.storageType === "file" && isURL && fileURL) {
721707
await storageClient.send(
722708
new DeleteObjectCommand({
723709
Bucket: process.env.AWS_PROJECTFILES_BUCKET,
@@ -726,10 +712,13 @@ async function updateProjectFile(
726712
);
727713
}
728714

729-
const projectUpdate = await updateProjectFilesUtil(projectID, updated);
730-
if (!projectUpdate) {
731-
throw new Error("updatefail");
732-
}
715+
await ProjectFile.findOneAndUpdate(
716+
{
717+
projectID,
718+
fileID,
719+
},
720+
updateObj
721+
);
733722

734723
return res.send({
735724
err: false,
@@ -956,12 +945,12 @@ async function removeProjectFile(
956945
}).lean();
957946

958947
objsToDelete.push(found);
959-
if(children.length > 0) {
960-
objsToDelete.push(...children);
948+
if (children.length > 0) {
949+
objsToDelete.push(...children);
961950
}
962951

963952
const objectIDs = objsToDelete.map((obj) => obj.fileID);
964-
953+
965954
const filesToDelete = objsToDelete
966955
.map((obj) => {
967956
if (obj.storageType === "file") {

0 commit comments

Comments
 (0)