Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 174 lines 5.5 kB view raw
1import { ItemTypeKind } from '@roostorg/types'; 2import _ from 'lodash'; 3import sequelize, { 4 type BelongsToGetAssociationMixin, 5 type HasManyAddAssociationsMixin, 6 type HasManyGetAssociationsMixin, 7 type HasManyGetAssociationsMixinOptions, 8 type HasManySetAssociationsMixin, 9 type InferAttributes, 10 type InferCreationAttributes, 11 type Sequelize, 12} from 'sequelize'; 13 14import { type ItemSchema } from '../../services/moderationConfigService/index.js'; 15import { type DataTypes } from '../index.js'; 16import { type Org } from '../OrgModel.js'; 17import { type SequelizeAction } from './ActionModel.js'; 18import { type Rule, type RuleWithLatestVersion } from './RuleModel.js'; 19 20const { Model } = sequelize; 21 22export type ItemType = InstanceType<ReturnType<typeof makeItemTypeModel>>; 23 24const makeItemTypeModel = (sequelize: Sequelize, DataTypes: DataTypes) => { 25 class ItemType extends Model< 26 InferAttributes<ItemType>, 27 InferCreationAttributes<ItemType> 28 > { 29 public declare id: string; 30 public declare name: string; 31 public declare description?: string | null; 32 public declare fields: ItemSchema; 33 public declare getRules: HasManyGetAssociationsMixin<Rule>; 34 35 public declare orgId: string; 36 public declare getOrg: BelongsToGetAssociationMixin<Org>; 37 public declare kind: ItemTypeKind; 38 39 public declare addActions: HasManyAddAssociationsMixin< 40 SequelizeAction, 41 string 42 >; 43 public declare setActions: HasManySetAssociationsMixin< 44 SequelizeAction, 45 string 46 >; 47 private declare getActionsSequelizeImpl: HasManyGetAssociationsMixin<SequelizeAction>; 48 49 // eslint-disable-next-line @typescript-eslint/no-explicit-any 50 static associate(models: { [key: string]: any }) { 51 ItemType.belongsTo(models.Org, { as: 'Org' }); 52 ItemType.belongsToMany(models.Rule, { 53 through: 'rules_and_item_types', 54 foreignKey: 'item_type_id', 55 as: 'Rules', 56 }); 57 58 const actionAssoc = ItemType.belongsToMany(models.Action, { 59 through: 'actions_and_item_types', 60 foreignKey: 'item_type_id', 61 as: 'Actions', 62 }); 63 Object.defineProperty( 64 models.ItemType.prototype, 65 'getActionsSequelizeImpl', 66 { 67 enumerable: false, 68 value(...params: unknown[]) { 69 // eslint-disable-next-line @typescript-eslint/no-explicit-any 70 return (actionAssoc as any)['get'](this, ...params); 71 }, 72 }, 73 ); 74 } 75 76 /** 77 * This function returns the list of rules that are "enabled", meaning that 78 * we'd run them against a new piece of content of this content type, if the 79 * content were submitted right now. "Running the rule" just means checking 80 * if its conditions pass on the content; whether we'd run the actions of 81 * each passing rule is a different question. 82 * 83 * This function is _highly_ impure. Its results will change as rules 84 * expire, or as daily limits on rules are reached, among other things. As 85 * we see more use cases, we might wanna refactor where this lives. 86 */ 87 async getEnabledRules() { 88 return this.getRules({ 89 scope: 'enabled', 90 include: ['latestVersion'], 91 }) as Promise<RuleWithLatestVersion[]>; 92 } 93 94 async getActions( 95 options?: HasManyGetAssociationsMixinOptions, 96 ): Promise<SequelizeAction[]> { 97 return sequelize.transaction(async () => { 98 const [explicitlyAssociatedActions, allActions] = await Promise.all([ 99 this.getActionsSequelizeImpl(options), 100 this.sequelize.model('action').findAll({ 101 ...options, 102 where: { 103 ...options?.where, 104 orgId: this.orgId, 105 }, 106 }), 107 ]); 108 109 return _.uniqBy( 110 [ 111 // Do this filter outside of the sequelize query because Sequelize 112 // assumes the name of the enum and doesn't allow you to set it 113 ...(allActions as SequelizeAction[]).filter((it) => 114 it.appliesToAllItemsOfKind.includes(this.kind), 115 ), 116 ...explicitlyAssociatedActions, 117 ], 118 (it) => it.id, 119 ); 120 }); 121 } 122 } 123 124 /* Fields */ 125 ItemType.init( 126 { 127 id: { 128 type: DataTypes.STRING, 129 primaryKey: true, 130 }, 131 orgId: { 132 type: DataTypes.STRING, 133 allowNull: false, 134 }, 135 // Name of the item type, which must unique within each Org 136 name: { 137 type: DataTypes.STRING, 138 allowNull: false, 139 validate: { notEmpty: true }, 140 }, 141 description: { 142 type: DataTypes.STRING, 143 allowNull: true, 144 }, 145 fields: { 146 type: DataTypes.ARRAY(DataTypes.JSONB), 147 allowNull: false, 148 validate: { 149 notEmpty: true, 150 }, 151 }, 152 kind: { 153 type: DataTypes.ENUM(...Object.values(ItemTypeKind)), 154 allowNull: false, 155 defaultValue: ItemTypeKind.CONTENT, 156 }, 157 }, 158 { 159 sequelize, 160 // legacy name; left as-is in case changing it will break auto-generated 161 // methods added by sequelize and calls to sequelize.model('content_type') 162 // and possibly many other things on which don't have great typescript 163 // support to check us. 164 modelName: 'content_type', 165 underscored: true, 166 tableName: 'item_types', 167 updatedAt: false, 168 }, 169 ); 170 171 return ItemType; 172}; 173 174export default makeItemTypeModel;