Ctrl K
ComunidadGuías…

server/models/Group.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type { InferAttributes, InferCreationAttributes } from "sequelize";
import { Op, Sequelize } from "sequelize";
import {
  BelongsTo,
  Column,
  ForeignKey,
  Table,
  HasMany,
  BelongsToMany,
  DataType,
  Scopes,
} from "sequelize-typescript";
import { GroupValidation } from "@shared/validations";
import ExternalGroup from "./ExternalGroup";
import GroupMembership from "./GroupMembership";
import GroupUser from "./GroupUser";
import Team from "./Team";
import User from "./User";
import ParanoidModel from "./base/ParanoidModel";
import { CounterCache } from "./decorators/CounterCache";
import IsHexColor from "./validators/IsHexColor";
import Length from "./validators/Length";
import NotContainsUrl from "./validators/NotContainsUrl";

@Scopes(() => ({
  withMembership: (userId: string) => ({
    include: [
      {
        association: "groupUsers",
        required: true,
        where: {
          userId,
        },
      },
    ],
  }),
}))
@Table({
  tableName: "groups",
  modelName: "group",
  validate: {
    // Group names must be unique within the team, ignoring case. Groups synced
    // from an external provider are scoped by their externalId, since providers
    // can legitimately report several groups with the same name.
    async isUniqueNameInTeam(this: Group) {
      const foundItem = await Group.findOne({
        attributes: ["id"],
        where: {
          teamId: this.teamId,
          externalId: this.externalId ?? null,
          id: {
            [Op.not]: this.id,
          },
          [Op.and]: Sequelize.where(
            Sequelize.fn("lower", Sequelize.col("name")),
            (this.name ?? "").toLowerCase()
          ),
        },
      });

      if (foundItem) {
        throw new Error("The name of this group is already in use");
      }
    },
  },
})
class Group extends ParanoidModel<
  InferAttributes<Group>,
  Partial<InferCreationAttributes<Group>>
> {
  @Length({
    min: 0,
    max: GroupValidation.maxNameLength,
    msg: `name must be ${GroupValidation.maxNameLength} characters or less`,
  })
  @NotContainsUrl
  @Column(DataType.STRING)
  name: string;

  @Length({
    min: 0,
    max: GroupValidation.maxDescriptionLength,
    msg: `description must be ${GroupValidation.maxDescriptionLength} characters or less`,
  })
  @Column(DataType.TEXT)
  description: string;

  /** An icon (or) emoji to use as the group icon. */
  @Column(DataType.STRING)
  icon: string | null;

  /** The color of the icon. */
  @IsHexColor
  @Column(DataType.STRING)
  color: string | null;

  @Column(DataType.STRING)
  externalId: string;

  @Column(DataType.BOOLEAN)
  disableMentions: boolean;

  static filterByMember(userId: string | undefined) {
    return userId
      ? this.scope({ method: ["withMembership", userId] })
      : this.scope("defaultScope");
  }

  // associations

  @HasMany(() => GroupUser, "groupId")
  groupUsers: GroupUser[];

  @HasMany(() => ExternalGroup, "groupId")
  externalGroups: ExternalGroup[];

  @HasMany(() => GroupMembership, "groupId")
  groupMemberships: GroupMembership[];

  @BelongsTo(() => Team, "teamId")
  team: Team;

  @ForeignKey(() => Team)
  @Column(DataType.UUID)
  teamId: string;

  @BelongsTo(() => User, "createdById")
  createdBy: User;

  @ForeignKey(() => User)
  @Column(DataType.UUID)
  createdById: string;

  @BelongsToMany(() => User, () => GroupUser)
  users: User[];

  @CounterCache(() => GroupUser, {
    as: "members",
    foreignKey: "groupId",
    include: [
      {
        association: "user",
        required: true,
        attributes: [],
        where: {
          suspendedAt: { [Op.is]: null },
        },
      },
    ],
  })
  memberCount: Promise<number>;
}

export default Group;