mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-18 20:59:55 -05:00
feat(collections): add many models to a collection in one call
Add POST /collection/:id/items. Thingport Grab uses it to file a MakerWorld collection's already-imported models instead of one request per model, falling back on older instances.
This commit is contained in:
1 parent
623cf25f2c
commit
acfa844ebe
3 files changed
+68
-3
No files matched your search
@@ -245,6 +245,38 @@ router.post(
|
||||
}),
|
||||
);
|
||||
|
||||
const addItemsSchema = z.object({ print_ids: z.array(z.string()).max(10_000) });
|
||||
|
||||
// Many at once, e.g. Thingport Grab filing a provider collection's already-imported models.
|
||||
router.post(
|
||||
"/collection/:id/items",
|
||||
asyncHandler(async (req, res) => {
|
||||
if (isSystemCollectionId(req.params.id)) {
|
||||
throw new HttpError(400, "This collection can't be edited");
|
||||
}
|
||||
const body = parseBody(addItemsSchema, req.body);
|
||||
const collection = await prisma.collection.findFirst({ where: { id: req.params.id, userId: req.userId } });
|
||||
if (!collection) throw new HttpError(404, "Collection not found");
|
||||
const prints = await prisma.print.findMany({
|
||||
where: { id: { in: body.print_ids }, userId: req.userId },
|
||||
select: { id: true },
|
||||
});
|
||||
await addPrintsToCollection(
|
||||
collection.id,
|
||||
prints.map((p) => p.id),
|
||||
);
|
||||
res.json({ ok: true });
|
||||
if (prints.length) {
|
||||
void createLog({
|
||||
userId: req.userId!,
|
||||
action: "collection_item_added",
|
||||
targetId: collection.id,
|
||||
details: { name: collection.name, count: prints.length },
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/collection/:id/bookmark",
|
||||
asyncHandler(async (req, res) => {
|
||||
|
||||
@@ -155,6 +155,34 @@ describe("removing one item from a collection", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("adding many items to a collection at once", () => {
|
||||
it("adds the caller's own prints, skipping unknown ids and ones already in it", async () => {
|
||||
const uploads = await Promise.all(
|
||||
["bulk-add-1", "bulk-add-2"].map((name) =>
|
||||
request(app)
|
||||
.post("/api/upload")
|
||||
.set(auth())
|
||||
.attach("files", tmpFile(`${name}.stl`, `solid ${name} endsolid`)),
|
||||
),
|
||||
);
|
||||
const [first, second] = uploads.map((res) => res.body.prints[0].id as string);
|
||||
const collection = await request(app).post("/api/collections").set(auth()).send({ name: "Bulk Add" });
|
||||
await addPrintsToCollection(collection.body.id, [first]);
|
||||
|
||||
const res = await request(app)
|
||||
.post(`/api/collection/${collection.body.id}/items`)
|
||||
.set(auth())
|
||||
.send({ print_ids: [first, second, "not-a-print"] });
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
const after = await request(app).get(`/api/collection/${collection.body.id}`).set(auth());
|
||||
expect(after.body.item_count).toBe(2);
|
||||
|
||||
for (const id of [first, second]) await request(app).delete(`/api/print/${id}`).set(auth());
|
||||
await request(app).delete(`/api/collection/${collection.body.id}`).set(auth());
|
||||
});
|
||||
});
|
||||
|
||||
describe("collection/print deletion cascades", () => {
|
||||
it("deleting a collection does not delete the models in it", async () => {
|
||||
const uploadRes = await request(app)
|
||||
|
||||
@@ -244,9 +244,14 @@ async function startGuidedImport(
|
||||
`Adding ${alreadyImported.length} existing model${alreadyImported.length === 1 ? "" : "s"} to your collection…`,
|
||||
),
|
||||
);
|
||||
for (const entry of alreadyImported) {
|
||||
if (entry.print_id)
|
||||
await api("POST", `/collection/${collectionId}/items/${entry.print_id}`).catch(() => undefined);
|
||||
const printIds = alreadyImported.flatMap((entry) => (entry.print_id ? [entry.print_id] : []));
|
||||
try {
|
||||
await api("POST", `/collection/${collectionId}/items`, { print_ids: printIds });
|
||||
} catch {
|
||||
// Instances without the bulk endpoint, one model at a time.
|
||||
for (const printId of printIds) {
|
||||
await api("POST", `/collection/${collectionId}/items/${printId}`).catch(() => undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user