Compare commits

...

5 Commits

Author SHA1 Message Date
hmacr cde0edfc67 cache changeset before all create and update flows 2025-01-17 08:03:30 +05:30
hmacr e566926720 handle group memberships 2025-01-17 07:09:50 +05:30
hmacr ae4a7d7166 handle reactions 2025-01-17 06:52:22 +05:30
hmacr ebb92cfa88 remove namespace 2025-01-17 06:52:22 +05:30
hmacr 33bd2950be groupuser namespace 2025-01-17 06:52:22 +05:30
17 changed files with 88 additions and 134 deletions
-2
View File
@@ -36,8 +36,6 @@ class Attachment extends IdModel<
InferAttributes<Attachment>,
Partial<InferCreationAttributes<Attachment>>
> {
static eventNamespace = "attachments";
@Length({
max: 4096,
msg: "key must be 4096 characters or less",
-2
View File
@@ -166,8 +166,6 @@ class Collection extends ParanoidModel<
InferAttributes<Collection>,
Partial<InferCreationAttributes<Collection>>
> {
static eventNamespace = "collections";
@SimpleLength({
min: 10,
max: 10,
-2
View File
@@ -42,8 +42,6 @@ class Comment extends ParanoidModel<
InferAttributes<Comment>,
Partial<InferCreationAttributes<Comment>>
> {
static eventNamespace = "comments";
@TextLength({
max: CommentValidation.maxLength,
msg: `Comment must be less than ${CommentValidation.maxLength} characters`,
-2
View File
@@ -254,8 +254,6 @@ class Document extends ArchivableModel<
InferAttributes<Document>,
Partial<InferCreationAttributes<Document>>
> {
static eventNamespace = "documents";
@SimpleLength({
min: 10,
max: 10,
-2
View File
@@ -60,8 +60,6 @@ class Group extends ParanoidModel<
InferAttributes<Group>,
Partial<InferCreationAttributes<Group>>
> {
static eventNamespace = "groups";
@Length({ min: 0, max: 255, msg: "name must be be 255 characters or less" })
@NotContainsUrl
@Column
+32 -39
View File
@@ -21,6 +21,7 @@ import {
AfterDestroy,
} from "sequelize-typescript";
import { CollectionPermission, DocumentPermission } from "@shared/types";
import { APIContext } from "@server/types";
import Collection from "./Collection";
import Document from "./Document";
import Group from "./Group";
@@ -211,20 +212,12 @@ class GroupMembership extends ParanoidModel<
@AfterCreate
static async publishAddGroupEventAfterCreate(
model: GroupMembership,
context: HookContext
context: APIContext["context"]
) {
const data = { membershipId: model.id, isNew: true };
const ctxWithData = {
...context,
event: { ...context.event, data },
} as HookContext;
if (model.collectionId) {
await Collection.insertEvent("add_group", model, ctxWithData);
} else {
await Document.insertEvent("add_group", model, ctxWithData);
}
await model.insertEvent(context, "add_group", {
membershipId: model.id,
isNew: true,
});
}
@AfterUpdate
@@ -257,20 +250,12 @@ class GroupMembership extends ParanoidModel<
@AfterUpdate
static async publishAddGroupEventAfterUpdate(
model: GroupMembership,
context: HookContext
context: APIContext["context"]
) {
const data = { membershipId: model.id, isNew: false };
const ctxWithData = {
...context,
event: { ...context.event, data },
} as HookContext;
if (model.collectionId) {
await Collection.insertEvent("add_group", model, ctxWithData);
} else {
await Document.insertEvent("add_group", model, ctxWithData);
}
await model.insertEvent(context, "add_group", {
membershipId: model.id,
isNew: false,
});
}
@AfterDestroy
@@ -295,20 +280,11 @@ class GroupMembership extends ParanoidModel<
@AfterDestroy
static async publishRemoveGroupEvent(
model: GroupMembership,
context: HookContext
context: APIContext["context"]
) {
const data = { membershipId: model.id };
const ctxWithData = {
...context,
event: { ...context.event, data },
} as HookContext;
if (model.collectionId) {
await Collection.insertEvent("remove_group", model, ctxWithData);
} else {
await Document.insertEvent("remove_group", model, ctxWithData);
}
await model.insertEvent(context, "remove_group", {
membershipId: model.id,
});
}
/**
@@ -372,6 +348,23 @@ class GroupMembership extends ParanoidModel<
);
}
}
private async insertEvent(
ctx: APIContext["context"],
name: string,
data: Record<string, unknown>
) {
const hookContext = {
...ctx,
event: { name, data, create: true },
} as HookContext;
if (this.collectionId) {
await Collection.insertEvent(name, this, hookContext);
} else {
await Document.insertEvent(name, this, hookContext);
}
}
}
export default GroupMembership;
+3 -21
View File
@@ -7,12 +7,10 @@ import {
Table,
DataType,
Scopes,
AfterCreate,
AfterDestroy,
} from "sequelize-typescript";
import Group from "./Group";
import User from "./User";
import Model, { type HookContext } from "./base/Model";
import Model from "./base/Model";
import Fix from "./decorators/Fix";
@DefaultScope(() => ({
@@ -44,6 +42,8 @@ class GroupUser extends Model<
InferAttributes<GroupUser>,
Partial<InferCreationAttributes<GroupUser>>
> {
static eventNamespace = "groups";
@BelongsTo(() => User, "userId")
user: User;
@@ -68,24 +68,6 @@ class GroupUser extends Model<
get modelId() {
return this.groupId;
}
// hooks
@AfterCreate
public static async publishAddUserEvent(
model: GroupUser,
context: HookContext
) {
await Group.insertEvent("add_user", model, context);
}
@AfterDestroy
public static async publishRemoveUserEvent(
model: GroupUser,
context: HookContext
) {
await Group.insertEvent("remove_user", model, context);
}
}
export default GroupUser;
-2
View File
@@ -20,8 +20,6 @@ class Pin extends IdModel<
InferAttributes<Pin>,
Partial<InferCreationAttributes<Pin>>
> {
static eventNamespace = "pins";
@Length({
max: 256,
msg: `index must be 256 characters or less`,
+3 -3
View File
@@ -18,10 +18,10 @@ import {
Table,
} from "sequelize-typescript";
import { createContext } from "@server/context";
import { APIContext } from "@server/types";
import Comment from "./Comment";
import User from "./User";
import IdModel from "./base/IdModel";
import { type HookContext } from "./base/Model";
import Fix from "./decorators/Fix";
import Length from "./validators/Length";
@@ -57,7 +57,7 @@ class Reaction extends IdModel<
@AfterCreate
public static async addReactionToCommentCache(
model: Reaction,
ctx: HookContext &
ctx: APIContext["context"] &
FindOrCreateOptions<Attributes<Reaction>, CreationAttributes<Reaction>>
) {
const { transaction } = ctx;
@@ -109,7 +109,7 @@ class Reaction extends IdModel<
@AfterDestroy
public static async removeReactionFromCommentCache(
model: Reaction,
ctx: HookContext & InstanceDestroyOptions
ctx: APIContext["context"] & InstanceDestroyOptions
) {
const { transaction } = ctx;
-2
View File
@@ -83,8 +83,6 @@ class Share extends IdModel<
InferAttributes<Share>,
Partial<InferCreationAttributes<Share>>
> {
static eventNamespace = "shares";
@Column
published: boolean;
-2
View File
@@ -19,8 +19,6 @@ class Star extends IdModel<
InferAttributes<Star>,
Partial<InferCreationAttributes<Star>>
> {
static eventNamespace = "stars";
@Length({
max: 256,
msg: `index must be 256 characters or less`,
-2
View File
@@ -28,8 +28,6 @@ class Subscription extends ParanoidModel<
InferAttributes<Subscription>,
Partial<InferCreationAttributes<Subscription>>
> {
static eventNamespace = "subscriptions";
@BelongsTo(() => User, "userId")
user: User;
+6 -10
View File
@@ -19,7 +19,7 @@ import {
AfterRestore,
AfterUpdate,
AfterUpsert,
BeforeCreate,
BeforeSave,
Model as SequelizeModel,
} from "sequelize-typescript";
import Logger from "@server/logging/Logger";
@@ -47,8 +47,7 @@ class Model<
TCreationAttributes extends {} = TModelAttributes
> extends SequelizeModel<TModelAttributes, TCreationAttributes> {
/**
* The namespace to use for events, if none is provided an event will not be created
* during the migration period. In the future this may default to the table name.
* The namespace to use for events - defaults to the table name if none is provided.
*/
static eventNamespace: string | undefined;
@@ -67,7 +66,6 @@ class Model<
create: true,
},
};
this.cacheChangeset();
return this.save({ ...options, ...hookContext });
}
@@ -87,7 +85,6 @@ class Model<
},
};
this.set(keys);
this.cacheChangeset();
return this.save(hookContext);
}
@@ -162,8 +159,8 @@ class Model<
return this.create(values, hookContext);
}
@BeforeCreate
static async beforeCreateEvent<T extends Model>(model: T) {
@BeforeSave
static async beforeSaveEvent<T extends Model>(model: T) {
model.cacheChangeset();
}
@@ -219,11 +216,10 @@ class Model<
model: T,
context: HookContext
) {
const namespace = this.eventNamespace;
const namespace = this.eventNamespace ?? this.tableName;
const models = this.sequelize!.models;
// If no namespace is defined, don't create an event
if (!namespace || !context.event?.create) {
if (!context.event?.create) {
return;
}
+14 -16
View File
@@ -211,24 +211,22 @@ router.post(
authorize(user, "update", collection);
authorize(user, "read", group);
const [membership, created] = await GroupMembership.findOrCreateWithCtx(
ctx,
{
where: {
collectionId: id,
groupId,
},
defaults: {
permission,
createdById: user.id,
},
lock: transaction.LOCK.UPDATE,
}
);
const [membership, created] = await GroupMembership.findOrCreate({
where: {
collectionId: id,
groupId,
},
defaults: {
permission,
createdById: user.id,
},
lock: transaction.LOCK.UPDATE,
...ctx.context,
});
if (!created) {
membership.permission = permission;
await membership.saveWithCtx(ctx);
await membership.save(ctx.context);
}
const groupMemberships = [presentGroupMembership(membership)];
@@ -273,7 +271,7 @@ router.post(
ctx.throw(400, "This Group is not a part of the collection");
}
await membership.destroyWithCtx(ctx);
await membership.destroy(ctx.context);
ctx.body = {
success: true,
+3 -2
View File
@@ -375,12 +375,13 @@ router.post(
authorize(user, "comment", document);
authorize(user, "addReaction", comment);
await Reaction.findOrCreateWithCtx(ctx, {
await Reaction.findOrCreate({
where: {
emoji,
userId: user.id,
commentId: id,
},
...ctx.context,
});
ctx.body = {
@@ -423,7 +424,7 @@ router.post(
});
authorize(user, "delete", reaction);
await reaction.destroyWithCtx(ctx);
await reaction.destroy(ctx.context);
ctx.body = {
success: true,
+14 -16
View File
@@ -1839,20 +1839,18 @@ router.post(
authorize(user, "update", document);
authorize(user, "read", group);
const [membership, created] = await GroupMembership.findOrCreateWithCtx(
ctx,
{
where: {
documentId: id,
groupId,
},
defaults: {
permission: permission || user.defaultDocumentPermission,
createdById: user.id,
},
lock: transaction.LOCK.UPDATE,
}
);
const [membership, created] = await GroupMembership.findOrCreate({
where: {
documentId: id,
groupId,
},
defaults: {
permission: permission || user.defaultDocumentPermission,
createdById: user.id,
},
lock: transaction.LOCK.UPDATE,
...ctx.context,
});
if (!created && permission) {
membership.permission = permission;
@@ -1860,7 +1858,7 @@ router.post(
// disconnect from the source if the permission is manually updated
membership.sourceId = null;
await membership.saveWithCtx(ctx);
await membership.save(ctx.context);
}
ctx.body = {
@@ -1905,7 +1903,7 @@ router.post(
rejectOnEmpty: true,
});
await membership.destroyWithCtx(ctx);
await membership.destroy(ctx.context);
ctx.body = {
success: true,
+13 -9
View File
@@ -261,15 +261,19 @@ router.post(
const group = await Group.findByPk(id, { transaction });
authorize(actor, "update", group);
const [groupUser] = await GroupUser.findOrCreateWithCtx(ctx, {
where: {
groupId: group.id,
userId: user.id,
const [groupUser] = await GroupUser.findOrCreateWithCtx(
ctx,
{
where: {
groupId: group.id,
userId: user.id,
},
defaults: {
createdById: actor.id,
},
},
defaults: {
createdById: actor.id,
},
});
{ name: "add_user" }
);
groupUser.user = user;
@@ -308,7 +312,7 @@ router.post(
lock: transaction.LOCK.UPDATE,
});
await groupUser?.destroyWithCtx(ctx);
await groupUser?.destroyWithCtx(ctx, { name: "remove_user" });
ctx.body = {
data: {