mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
fix: Outdated updatedBy returned in document mutations (#7223)
* fix: Outdated updatedBy information returned in document mutations * tsc * Update delete mutations to match
This commit is contained in:
@@ -244,7 +244,7 @@ async function provisionFirstCollection(team: Team, user: User) {
|
||||
|
||||
document.content = await DocumentHelper.toJSON(document);
|
||||
|
||||
await document.publish(collection.createdById, collection.id, {
|
||||
await document.publish(user, collection.id, {
|
||||
transaction,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ export default async function documentCreator({
|
||||
throw new Error("Collection ID is required to publish");
|
||||
}
|
||||
|
||||
await document.publish(user.id, collectionId, { transaction });
|
||||
await document.publish(user, collectionId, { transaction });
|
||||
await Event.create(
|
||||
{
|
||||
name: "documents.publish",
|
||||
|
||||
@@ -110,7 +110,7 @@ export default async function documentUpdater({
|
||||
if (!document.collectionId) {
|
||||
document.collectionId = cId;
|
||||
}
|
||||
await document.publish(user.id, cId, { transaction });
|
||||
await document.publish(user, cId, { transaction });
|
||||
|
||||
await Event.create(
|
||||
{
|
||||
@@ -121,6 +121,7 @@ export default async function documentUpdater({
|
||||
);
|
||||
} else if (changed) {
|
||||
document.lastModifiedById = user.id;
|
||||
document.updatedBy = user;
|
||||
await document.save({ transaction });
|
||||
|
||||
await Event.create(event, { transaction });
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("#delete", () => {
|
||||
test("should soft delete and set last modified", async () => {
|
||||
const document = await buildDocument();
|
||||
const user = await buildUser();
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
|
||||
const newDocument = await Document.findByPk(document.id, {
|
||||
paranoid: false,
|
||||
@@ -54,7 +54,7 @@ describe("#delete", () => {
|
||||
template: true,
|
||||
});
|
||||
const user = await buildUser();
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const newDocument = await Document.findByPk(document.id, {
|
||||
paranoid: false,
|
||||
});
|
||||
@@ -67,7 +67,7 @@ describe("#delete", () => {
|
||||
archivedAt: new Date(),
|
||||
});
|
||||
const user = await buildUser();
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const newDocument = await Document.findByPk(document.id, {
|
||||
paranoid: false,
|
||||
});
|
||||
@@ -78,7 +78,7 @@ describe("#delete", () => {
|
||||
it("should delete draft without collection", async () => {
|
||||
const user = await buildUser();
|
||||
const document = await buildDraftDocument();
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const deletedDocument = await Document.findByPk(document.id, {
|
||||
paranoid: false,
|
||||
});
|
||||
|
||||
+50
-50
@@ -808,37 +808,8 @@ class Document extends ParanoidModel<
|
||||
return findAllChildDocumentIds(this.id);
|
||||
};
|
||||
|
||||
archiveWithChildren = async (
|
||||
userId: string,
|
||||
options?: FindOptions<Document>
|
||||
) => {
|
||||
const archivedAt = new Date();
|
||||
|
||||
// Helper to archive all child documents for a document
|
||||
const archiveChildren = async (parentDocumentId: string) => {
|
||||
const childDocuments = await (
|
||||
this.constructor as typeof Document
|
||||
).findAll({
|
||||
where: {
|
||||
parentDocumentId,
|
||||
},
|
||||
});
|
||||
for (const child of childDocuments) {
|
||||
await archiveChildren(child.id);
|
||||
child.archivedAt = archivedAt;
|
||||
child.lastModifiedById = userId;
|
||||
await child.save(options);
|
||||
}
|
||||
};
|
||||
|
||||
await archiveChildren(this.id);
|
||||
this.archivedAt = archivedAt;
|
||||
this.lastModifiedById = userId;
|
||||
return this.save(options);
|
||||
};
|
||||
|
||||
publish = async (
|
||||
userId: string,
|
||||
user: User,
|
||||
collectionId: string,
|
||||
{ transaction }: SaveOptions<Document>
|
||||
) => {
|
||||
@@ -890,7 +861,8 @@ class Document extends ParanoidModel<
|
||||
)
|
||||
);
|
||||
|
||||
this.lastModifiedById = userId;
|
||||
this.lastModifiedById = user.id;
|
||||
this.updatedBy = user;
|
||||
this.publishedAt = new Date();
|
||||
return this.save({ transaction });
|
||||
};
|
||||
@@ -911,9 +883,8 @@ class Document extends ParanoidModel<
|
||||
return false;
|
||||
};
|
||||
|
||||
unpublish = async (userId: string) => {
|
||||
// If the document is already a draft then calling unpublish should act like
|
||||
// a regular save
|
||||
unpublish = async (user: User) => {
|
||||
// If the document is already a draft then calling unpublish should act like save
|
||||
if (!this.publishedAt) {
|
||||
return this.save();
|
||||
}
|
||||
@@ -934,15 +905,17 @@ class Document extends ParanoidModel<
|
||||
|
||||
// unpublishing a document converts the ownership to yourself, so that it
|
||||
// will appear in your drafts rather than the original creators
|
||||
this.createdById = userId;
|
||||
this.lastModifiedById = userId;
|
||||
this.createdById = user.id;
|
||||
this.lastModifiedById = user.id;
|
||||
this.createdBy = user;
|
||||
this.updatedBy = user;
|
||||
this.publishedAt = null;
|
||||
return this.save();
|
||||
};
|
||||
|
||||
// Moves a document from being visible to the team within a collection
|
||||
// to the archived area, where it can be subsequently restored.
|
||||
archive = async (userId: string) => {
|
||||
archive = async (user: User) => {
|
||||
await this.sequelize.transaction(async (transaction: Transaction) => {
|
||||
const collection = this.collectionId
|
||||
? await Collection.findByPk(this.collectionId, {
|
||||
@@ -957,12 +930,12 @@ class Document extends ParanoidModel<
|
||||
}
|
||||
});
|
||||
|
||||
await this.archiveWithChildren(userId);
|
||||
await this.archiveWithChildren(user);
|
||||
return this;
|
||||
};
|
||||
|
||||
// Restore an archived document back to being visible to the team
|
||||
unarchive = async (userId: string) => {
|
||||
unarchive = async (user: User) => {
|
||||
await this.sequelize.transaction(async (transaction: Transaction) => {
|
||||
const collection = this.collectionId
|
||||
? await Collection.findByPk(this.collectionId, {
|
||||
@@ -997,13 +970,14 @@ class Document extends ParanoidModel<
|
||||
}
|
||||
|
||||
this.archivedAt = null;
|
||||
this.lastModifiedById = userId;
|
||||
this.lastModifiedById = user.id;
|
||||
this.updatedBy = user;
|
||||
await this.save();
|
||||
return this;
|
||||
};
|
||||
|
||||
// Delete a document, archived or otherwise.
|
||||
delete = (userId: string) =>
|
||||
delete = (user: User) =>
|
||||
this.sequelize.transaction(async (transaction: Transaction) => {
|
||||
if (!this.archivedAt && !this.template && this.collectionId) {
|
||||
// delete any children and remove from the document structure
|
||||
@@ -1025,15 +999,10 @@ class Document extends ParanoidModel<
|
||||
},
|
||||
transaction,
|
||||
});
|
||||
await this.update(
|
||||
{
|
||||
lastModifiedById: userId,
|
||||
},
|
||||
{
|
||||
transaction,
|
||||
}
|
||||
);
|
||||
return this;
|
||||
|
||||
this.lastModifiedById = user.id;
|
||||
this.updatedBy = user;
|
||||
return this.save({ transaction });
|
||||
});
|
||||
|
||||
getTimestamp = () => Math.round(new Date(this.updatedAt).getTime() / 1000);
|
||||
@@ -1098,6 +1067,37 @@ class Document extends ParanoidModel<
|
||||
children,
|
||||
};
|
||||
};
|
||||
|
||||
private archiveWithChildren = async (
|
||||
user: User,
|
||||
options?: FindOptions<Document>
|
||||
) => {
|
||||
const archivedAt = new Date();
|
||||
|
||||
// Helper to archive all child documents for a document
|
||||
const archiveChildren = async (parentDocumentId: string) => {
|
||||
const childDocuments = await (
|
||||
this.constructor as typeof Document
|
||||
).findAll({
|
||||
where: {
|
||||
parentDocumentId,
|
||||
},
|
||||
});
|
||||
for (const child of childDocuments) {
|
||||
await archiveChildren(child.id);
|
||||
child.archivedAt = archivedAt;
|
||||
child.lastModifiedById = user.id;
|
||||
child.updatedBy = user;
|
||||
await child.save(options);
|
||||
}
|
||||
};
|
||||
|
||||
await archiveChildren(this.id);
|
||||
this.archivedAt = archivedAt;
|
||||
this.lastModifiedById = user.id;
|
||||
this.updatedBy = user;
|
||||
return this.save(options);
|
||||
};
|
||||
}
|
||||
|
||||
export default Document;
|
||||
|
||||
@@ -87,7 +87,7 @@ describe("#documents.info", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.info", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -433,7 +433,7 @@ describe("#documents.info", () => {
|
||||
teamId: document.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.info", {
|
||||
body: {
|
||||
shareId: share.id,
|
||||
@@ -607,7 +607,7 @@ describe("#documents.export", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.export", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -1638,7 +1638,7 @@ describe("#documents.search", () => {
|
||||
text: "search term",
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.search", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -1658,7 +1658,7 @@ describe("#documents.search", () => {
|
||||
text: "search term",
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.search", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -1938,7 +1938,7 @@ describe("#documents.archived", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.archived", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -1955,7 +1955,7 @@ describe("#documents.archived", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const res = await server.post("/api/documents.archived", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -1975,7 +1975,7 @@ describe("#documents.archived", () => {
|
||||
teamId: user.teamId,
|
||||
collectionId: collection.id,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.archived", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -2009,7 +2009,7 @@ describe("#documents.deleted", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const res = await server.post("/api/documents.deleted", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -2041,9 +2041,9 @@ describe("#documents.deleted", () => {
|
||||
collectionId: null,
|
||||
});
|
||||
await Promise.all([
|
||||
document.delete(user.id),
|
||||
draftDocument.delete(user.id),
|
||||
otherUserDraft.delete(user2.id),
|
||||
document.delete(user),
|
||||
draftDocument.delete(user),
|
||||
otherUserDraft.delete(user2),
|
||||
]);
|
||||
const res = await server.post("/api/documents.deleted", {
|
||||
body: {
|
||||
@@ -2067,7 +2067,7 @@ describe("#documents.deleted", () => {
|
||||
teamId: user.teamId,
|
||||
collectionId: collection.id,
|
||||
});
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const res = await server.post("/api/documents.deleted", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -2476,7 +2476,7 @@ describe("#documents.restore", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const res = await server.post("/api/documents.restore", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -2560,7 +2560,7 @@ describe("#documents.restore", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.restore", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -2584,8 +2584,8 @@ describe("#documents.restore", () => {
|
||||
collectionId: document.collectionId,
|
||||
parentDocumentId: document.id,
|
||||
});
|
||||
await childDocument.archive(user.id);
|
||||
await document.archive(user.id);
|
||||
await childDocument.archive(user);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.restore", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -3286,7 +3286,7 @@ describe("#documents.update", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.update", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -3594,9 +3594,10 @@ describe("#documents.archive", () => {
|
||||
});
|
||||
|
||||
it("should allow archiving document", async () => {
|
||||
const user = await buildUser();
|
||||
const admin = await buildAdmin();
|
||||
const user = await buildUser({ teamId: admin.teamId });
|
||||
const document = await buildDocument({
|
||||
userId: user.id,
|
||||
userId: admin.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
const res = await server.post("/api/documents.archive", {
|
||||
@@ -3800,6 +3801,8 @@ describe("#documents.unpublish", () => {
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.id).toEqual(document.id);
|
||||
expect(body.data.publishedAt).toBeNull();
|
||||
expect(body.data.createdBy.id).toEqual(user.id);
|
||||
expect(body.data.updatedBy.id).toEqual(user.id);
|
||||
|
||||
const reloaded = await Document.unscoped().findByPk(document.id);
|
||||
expect(reloaded!.createdById).toEqual(user.id);
|
||||
@@ -3816,7 +3819,7 @@ describe("#documents.unpublish", () => {
|
||||
teamId: user.teamId,
|
||||
parentDocumentId: document.id,
|
||||
});
|
||||
await child.archive(user.id);
|
||||
await child.archive(user);
|
||||
const res = await server.post("/api/documents.unpublish", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -3827,6 +3830,7 @@ describe("#documents.unpublish", () => {
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.id).toEqual(document.id);
|
||||
expect(body.data.publishedAt).toBeNull();
|
||||
expect(body.data.updatedBy.id).toEqual(user.id);
|
||||
});
|
||||
|
||||
it("should unpublish another users document", async () => {
|
||||
@@ -3850,6 +3854,7 @@ describe("#documents.unpublish", () => {
|
||||
expect(res.status).toEqual(200);
|
||||
expect(body.data.id).toEqual(document.id);
|
||||
expect(body.data.publishedAt).toBeNull();
|
||||
expect(body.data.updatedBy.id).toEqual(user.id);
|
||||
|
||||
const reloaded = await Document.unscoped().findByPk(document.id);
|
||||
expect(reloaded!.createdById).toEqual(user.id);
|
||||
@@ -3876,7 +3881,7 @@ describe("#documents.unpublish", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const res = await server.post("/api/documents.unpublish", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -3892,7 +3897,7 @@ describe("#documents.unpublish", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
const res = await server.post("/api/documents.unpublish", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -4496,7 +4501,7 @@ describe("#documents.empty_trash", () => {
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
});
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
|
||||
const res = await server.post("/api/documents.empty_trash", {
|
||||
body: {
|
||||
|
||||
@@ -681,7 +681,7 @@ router.post(
|
||||
if (document.deletedAt) {
|
||||
authorize(user, "restore", document);
|
||||
// restore a previously deleted document
|
||||
await document.unarchive(user.id);
|
||||
await document.unarchive(user);
|
||||
await Event.createFromContext(ctx, {
|
||||
name: "documents.restore",
|
||||
documentId: document.id,
|
||||
@@ -693,7 +693,7 @@ router.post(
|
||||
} else if (document.archivedAt) {
|
||||
authorize(user, "unarchive", document);
|
||||
// restore a previously archived document
|
||||
await document.unarchive(user.id);
|
||||
await document.unarchive(user);
|
||||
await Event.createFromContext(ctx, {
|
||||
name: "documents.unarchive",
|
||||
documentId: document.id,
|
||||
@@ -1182,7 +1182,7 @@ router.post(
|
||||
});
|
||||
authorize(user, "archive", document);
|
||||
|
||||
await document.archive(user.id);
|
||||
await document.archive(user);
|
||||
await Event.createFromContext(ctx, {
|
||||
name: "documents.archive",
|
||||
documentId: document.id,
|
||||
@@ -1230,7 +1230,7 @@ router.post(
|
||||
|
||||
authorize(user, "delete", document);
|
||||
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
await Event.createFromContext(ctx, {
|
||||
name: "documents.delete",
|
||||
documentId: document.id,
|
||||
@@ -1271,7 +1271,7 @@ router.post(
|
||||
);
|
||||
}
|
||||
|
||||
await document.unpublish(user.id);
|
||||
await document.unpublish(user);
|
||||
await Event.createFromContext(ctx, {
|
||||
name: "documents.unpublish",
|
||||
documentId: document.id,
|
||||
|
||||
@@ -111,7 +111,7 @@ describe("#shares.list", () => {
|
||||
teamId: user.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const res = await server.post("/api/shares.list", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
@@ -966,7 +966,7 @@ describe("#shares.revoke", () => {
|
||||
teamId: user.teamId,
|
||||
userId: user.id,
|
||||
});
|
||||
await document.delete(user.id);
|
||||
await document.delete(user);
|
||||
const res = await server.post("/api/shares.revoke", {
|
||||
body: {
|
||||
token: user.getJwtToken(),
|
||||
|
||||
Reference in New Issue
Block a user