import type { IUserGroup } from '@growi/core'; import type { Model, Document } from 'mongoose'; import { Schema } from 'mongoose'; import mongoosePaginate from 'mongoose-paginate-v2'; import { getOrCreateModel } from '../util/mongoose-utils'; export interface UserGroupDocument extends IUserGroup, Document {} export interface UserGroupModel extends Model { [x:string]: any, // for old methods PAGE_ITEMS: 10, findGroupsWithDescendantsRecursively: (groups: UserGroupDocument[], descendants?: UserGroupDocument[]) => Promise, } /* * define schema */ const ObjectId = Schema.Types.ObjectId; const schema = new Schema({ name: { type: String, required: true, unique: true }, parent: { type: ObjectId, ref: 'UserGroup', index: true }, description: { type: String, default: '' }, }, { timestamps: true, }); schema.plugin(mongoosePaginate); const PAGE_ITEMS = 10; schema.statics.findWithPagination = function(opts) { const query = { parent: null }; const options = Object.assign({}, opts); if (options.page == null) { options.page = 1; } if (options.limit == null) { options.limit = PAGE_ITEMS; } return this.paginate(query, options) .catch((err) => { // debug('Error on pagination:', err); TODO: add logger }); }; schema.statics.findChildrenByParentIds = async function(parentIds: string[], includeGrandChildren = false) { const childUserGroups = await this.find({ parent: { $in: parentIds } }); let grandChildUserGroups: UserGroupDocument[] | null = null; if (includeGrandChildren) { const childUserGroupIds = childUserGroups.map(group => group._id); grandChildUserGroups = await this.find({ parent: { $in: childUserGroupIds } }); } return { childUserGroups, grandChildUserGroups, }; }; schema.statics.countUserGroups = function() { return this.estimatedDocumentCount(); }; schema.statics.createGroup = async function(name, description, parentId) { let parent: UserGroupDocument | null = null; if (parentId != null) { parent = await this.findOne({ _id: parentId }); if (parent == null) { throw Error('Parent does not exist.'); } } return this.create({ name, description, parent }); }; /** * Find all ancestor groups starting from the UserGroup of the initial "group". * Set "ancestors" as "[]" if the initial group is unnecessary as result. * @param groups UserGroupDocument * @param ancestors UserGroupDocument[] * @returns UserGroupDocument[] */ schema.statics.findGroupsWithAncestorsRecursively = async function(group, ancestors = [group]) { if (group == null) { return ancestors; } const parent = await this.findOne({ _id: group.parent }); if (parent == null) { return ancestors; } ancestors.unshift(parent); return this.findGroupsWithAncestorsRecursively(parent, ancestors); }; /** * TODO: use $graphLookup * Find all descendant groups starting from the UserGroups in the initial groups in "groups". * Set "descendants" as "[]" if the initial groups are unnecessary as result. * @param groups UserGroupDocument[] including at least one UserGroup * @param descendants UserGroupDocument[] * @returns UserGroupDocument[] */ schema.statics.findGroupsWithDescendantsRecursively = async function( groups: UserGroupDocument[], descendants: UserGroupDocument[] = groups, ): Promise { const nextGroups = await this.find({ parent: { $in: groups.map(g => g._id) } }); if (nextGroups.length === 0) { return descendants; } return this.findGroupsWithDescendantsRecursively(nextGroups, descendants.concat(nextGroups)); }; schema.statics.findGroupsWithDescendantsById = async function(groupId) { const root = await this.findOne({ _id: groupId }); if (root == null) { throw Error('The root user group does not exist'); } return this.findGroupsWithDescendantsRecursively([root]); }; export default getOrCreateModel('UserGroup', schema);