Taichi Masuyama 4 лет назад
Родитель
Сommit
f259c28659

+ 5 - 0
packages/app/src/server/models/user-group-relation.js

@@ -272,6 +272,11 @@ class UserGroupRelation {
       });
   }
 
+  static async findWithUserByGroupIds(groupIds = null) {
+    return this.find(Array.isArray(groupIds) ? { relatedGroup: { $in: groupIds } } : {})
+      .populate('relatedUser');
+  }
+
 }
 
 module.exports = function(crowi) {

+ 20 - 1
packages/app/src/server/models/user-group.js

@@ -63,7 +63,7 @@ class UserGroup {
    * @memberof UserGroup
    */
   static findUserGroupsWithPagination(opts) {
-    const query = {};
+    const query = { parent: null };
     const options = Object.assign({}, opts);
     if (options.page == null) {
       options.page = 1;
@@ -78,6 +78,25 @@ class UserGroup {
       });
   }
 
+  static async findChildUserGroupsByParentIds(parentIds, includeGrandChildren = false) {
+    if (parentIds == null) {
+      throw Error('parentIds must not be null.');
+    }
+
+    const childUserGroups = await this.find({ parent: { $in: parentIds } });
+
+    let grandChildUserGroups = null;
+    if (includeGrandChildren) {
+      const childUserGroupIds = childUserGroups.map(group => group._id);
+      grandChildUserGroups = await this.find({ parent: { $in: childUserGroupIds } });
+    }
+
+    return {
+      childUserGroups,
+      grandChildUserGroups,
+    };
+  }
+
   // Check if registerable
   static isRegisterableName(name) {
     const query = { name };

+ 19 - 61
packages/app/src/server/routes/apiv3/user-group-relation.js

@@ -3,12 +3,15 @@ import loggerFactory from '~/utils/logger';
 const logger = loggerFactory('growi:routes:apiv3:user-group-relation'); // eslint-disable-line no-unused-vars
 
 const express = require('express');
+const { query } = require('express-validator');
 
 const ErrorV3 = require('../../models/vo/error-apiv3');
 const { serializeUserGroupRelationSecurely } = require('../../models/serializers/user-group-relation-serializer');
 
 const router = express.Router();
 
+const validator = {};
+
 /**
  * @swagger
  *  tags:
@@ -21,6 +24,11 @@ module.exports = (crowi) => {
 
   const { UserGroupRelation } = crowi.models;
 
+  validator.list = [
+    query('groupIds', 'groupIds must be an array').optional().isArray(),
+    query('childGroupIds', 'childGroupIds must be an array').optional().isArray(),
+  ];
+
   /**
    * @swagger
    *  paths:
@@ -41,13 +49,21 @@ module.exports = (crowi) => {
    *                      type: object
    *                      description: contains arrays user objects related
    */
-  router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => { // TODO 85062: enable groupIds to filter
+  router.get('/', loginRequiredStrictly, adminRequired, validator.list, async(req, res) => {
+    const { query } = req;
+
     try {
-      const relations = await UserGroupRelation.find().populate('relatedUser');
+      const relations = await UserGroupRelation.findWithUserByGroupIds(query.groupIds);
+
+      let relationsOfChildGroups = null;
+      if (Array.isArray(query.childGroupIds)) {
+        const _relationsOfChildGroups = await UserGroupRelation.findWithUserByGroupIds(query.childGroupIds);
+        relationsOfChildGroups = _relationsOfChildGroups.map(relation => serializeUserGroupRelationSecurely(relation)); // serialize
+      }
 
       const serialized = relations.map(relation => serializeUserGroupRelationSecurely(relation));
 
-      return res.apiv3({ userGroupRelations: serialized });
+      return res.apiv3({ userGroupRelations: serialized, relationsOfChildGroups });
     }
     catch (err) {
       const msg = 'Error occurred in fetching user group relations';
@@ -58,61 +74,3 @@ module.exports = (crowi) => {
 
   return router;
 };
-
-// const MAX_PAGE_LIST = 50;
-
-// function createPager(total, limit, page, pagesCount, maxPageList) {
-//   const pager = {
-//     page,
-//     pagesCount,
-//     pages: [],
-//     total,
-//     previous: null,
-//     previousDots: false,
-//     next: null,
-//     nextDots: false,
-//   };
-
-//   if (page > 1) {
-//     pager.previous = page - 1;
-//   }
-
-//   if (page < pagesCount) {
-//     pager.next = page + 1;
-//   }
-
-//   let pagerMin = Math.max(1, Math.ceil(page - maxPageList / 2));
-//   let pagerMax = Math.min(pagesCount, Math.floor(page + maxPageList / 2));
-//   if (pagerMin === 1) {
-//     if (MAX_PAGE_LIST < pagesCount) {
-//       pagerMax = MAX_PAGE_LIST;
-//     }
-//     else {
-//       pagerMax = pagesCount;
-//     }
-//   }
-//   if (pagerMax === pagesCount) {
-//     if ((pagerMax - MAX_PAGE_LIST) < 1) {
-//       pagerMin = 1;
-//     }
-//     else {
-//       pagerMin = pagerMax - MAX_PAGE_LIST;
-//     }
-//   }
-
-//   pager.previousDots = null;
-//   if (pagerMin > 1) {
-//     pager.previousDots = true;
-//   }
-
-//   pager.nextDots = null;
-//   if (pagerMax < pagesCount) {
-//     pager.nextDots = true;
-//   }
-
-//   for (let i = pagerMin; i <= pagerMax; i++) {
-//     pager.pages.push(i);
-//   }
-
-//   return pager;
-// }

+ 24 - 6
packages/app/src/server/routes/apiv3/user-group.js

@@ -40,6 +40,11 @@ module.exports = (crowi) => {
     Page,
   } = crowi.models;
 
+  validator.listChildren = [
+    query('parentIds', 'parentIds must be an array').optional().isArray(),
+    query('includeGrandChildren', 'parentIds must be boolean').optional().isBoolean(),
+  ];
+
   /**
    * @swagger
    *
@@ -61,10 +66,10 @@ module.exports = (crowi) => {
    *                      type: object
    *                      description: a result of `UserGroup.find`
    */
-  router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => { // TODO 85062: userGroups with no parent
+  router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
     const { query } = req;
 
-    // TODO: filter with querystring
+    // TODO 85062: improve sort
     try {
       const page = query.page != null ? parseInt(query.page) : undefined;
       const limit = query.limit != null ? parseInt(query.limit) : undefined;
@@ -84,10 +89,23 @@ module.exports = (crowi) => {
     }
   });
 
-  /*
-   * TODO 85062: GET /children ?include-grand-children=boolean fetch all children by parent ids
-   * if include-grand-children=true, return grand children as well
-   */
+  // TODO 85062: improve sort
+  router.get('/children', loginRequiredStrictly, adminRequired, validator.listChildren, async(req, res) => {
+    try {
+      const { parentIds, includeGrandChildren = false } = req.query;
+
+      const userGroupsResult = await UserGroup.findChildUserGroupsByParentIds(parentIds, includeGrandChildren);
+      return res.apiv3({
+        childUserGroups: userGroupsResult.childUserGroups,
+        grandChildUserGroups: userGroupsResult.grandChildUserGroups,
+      });
+    }
+    catch (err) {
+      const msg = 'Error occurred in fetching child user group list';
+      logger.error(msg, err);
+      return res.apiv3Err(new ErrorV3(msg, 'child-user-group-list-fetch-failed'));
+    }
+  });
 
   validator.create = [
     body('name', 'Group name is required').trim().exists({ checkFalsy: true }),

+ 10 - 6
packages/app/src/stores/user-group.tsx

@@ -16,20 +16,24 @@ export const useSWRxUserGroupList = (initialData?: IUserGroupHasId[]): SWRRespon
   );
 };
 
-export const useSWRxChildUserGroupList = (parentIds?: string[], initialData?: IUserGroupHasId[]): SWRResponse<ChildUserGroupListResult, Error> => {
+export const useSWRxChildUserGroupList = (
+    parentIds?: string[], includeGrandChildren?: boolean, initialData?: IUserGroupHasId[],
+): SWRResponse<ChildUserGroupListResult, Error> => {
   return useSWRImmutable(
-    parentIds != null ? JSON.stringify(['/user-groups/children', parentIds]) : null,
-    (endpoint, parentIds) => apiv3Get(endpoint, { parentIds }).then(result => result.data),
+    parentIds != null ? ['/user-groups/children', parentIds, includeGrandChildren] : null,
+    (endpoint, parentIds, includeGrandChildren) => apiv3Get(endpoint, { parentIds, includeGrandChildren }).then(result => result.data),
     {
       fallbackData: initialData,
     },
   );
 };
 
-export const useSWRxUserGroupRelationList = (groupIds?: string[], initialData?: IUserGroupRelationHasId[]): SWRResponse<UserGroupRelationListResult, Error> => {
+export const useSWRxUserGroupRelationList = (
+    groupIds?: string[], childGroupIds?: string[], initialData?: IUserGroupRelationHasId[],
+): SWRResponse<UserGroupRelationListResult, Error> => {
   return useSWRImmutable(
-    groupIds != null ? JSON.stringify(['/user-group-relations', groupIds]) : null,
-    (endpoint, parentIds) => apiv3Get(endpoint, { parentIds }).then(result => result.data),
+    groupIds != null && childGroupIds != null ? ['/user-group-relations', groupIds, childGroupIds] : null,
+    (endpoint, parentIds, childGroupIds) => apiv3Get(endpoint, { parentIds, childGroupIds }).then(result => result.data),
     {
       fallbackData: initialData,
     },