Просмотр исходного кода

Merge branch 'fix-page-model-tests' into fix-api-to-use-granted-user-groups

Futa Arai 2 лет назад
Родитель
Сommit
c62b51ea7f

+ 2 - 1
apps/app/src/components/PageAlert/FixPageGrantAlert.tsx

@@ -9,6 +9,7 @@ import { apiv3Put } from '~/client/util/apiv3-client';
 import { toastError, toastSuccess } from '~/client/util/toastr';
 import { PageGrant, IPageGrantData } from '~/interfaces/page';
 import { IRecordApplicableGrant, IResIsGrantNormalizedGrantData } from '~/interfaces/page-grant';
+import { UserGroupDocument } from '~/server/models/user-group';
 import { useCurrentUser } from '~/stores/context';
 import { useSWRxApplicableGrant, useSWRxIsGrantNormalized, useSWRxCurrentPage } from '~/stores/page';
 
@@ -28,7 +29,7 @@ const FixPageGrantModal = (props: ModalProps): JSX.Element => {
   } = props;
 
   const [selectedGrant, setSelectedGrant] = useState<PageGrant>(PageGrant.GRANT_RESTRICTED);
-  const [selectedGroup, setSelectedGroup] = useState<{_id: string, name: string} | undefined>(undefined); // TODO: Typescriptize model
+  const [selectedGroup, setSelectedGroup] = useState<UserGroupDocument | undefined>(undefined);
 
   // Alert message state
   const [shouldShowModalAlert, setShowModalAlert] = useState<boolean>(false);

+ 7 - 0
apps/app/src/features/external-user-group/server/models/external-user-group-relation.ts

@@ -1,5 +1,6 @@
 import { Schema, Model, Document } from 'mongoose';
 
+import { ObjectIdLike } from '~/server/interfaces/mongoose-utils';
 import UserGroupRelation from '~/server/models/user-group-relation';
 
 import { getOrCreateModel } from '../../../../server/util/mongoose-utils';
@@ -15,6 +16,12 @@ export interface ExternalUserGroupRelationModel extends Model<ExternalUserGroupR
   PAGE_ITEMS: 50,
 
   removeAllByUserGroups: (groupsToDelete: ExternalUserGroupDocument[]) => Promise<any>,
+
+  findAllUserIdsForUserGroups: (userGroupIds: ObjectIdLike[]) => Promise<string[]>,
+
+  findGroupsWithDescendantsByGroupAndUser: (group: ExternalUserGroupDocument, user) => Promise<ExternalUserGroupDocument[]>,
+
+  countByGroupIdAndUser: (userGroupId: string, userData) => Promise<number>
 }
 
 const schema = new Schema<ExternalUserGroupRelationDocument, ExternalUserGroupRelationModel>({

+ 3 - 1
apps/app/src/interfaces/page-grant.ts

@@ -1,7 +1,9 @@
+import { UserGroupDocument } from '~/server/models/user-group';
+
 import { PageGrant, IPageGrantData } from './page';
 
 export type IDataApplicableGroup = {
-  applicableGroups?: {_id: string, name: string}[] // TODO: Typescriptize model
+  applicableGroups?: UserGroupDocument[]
 }
 
 export type IDataApplicableGrant = null | IDataApplicableGroup;

+ 2 - 9
apps/app/src/server/models/obsolete-page.js

@@ -4,6 +4,8 @@ import escapeStringRegexp from 'escape-string-regexp';
 import { PageGrant } from '~/interfaces/page';
 import loggerFactory from '~/utils/logger';
 
+import UserGroupRelation from './user-group-relation';
+
 
 // disable no-return-await for model functions
 /* eslint-disable no-return-await */
@@ -326,7 +328,6 @@ export const getPageSchema = (crowi) => {
     let userGroups = [];
     if (user != null) {
       validateCrowi();
-      const UserGroupRelation = crowi.model('UserGroupRelation');
       userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 
@@ -347,8 +348,6 @@ export const getPageSchema = (crowi) => {
 
     let relatedUserGroups = userGroups;
     if (user != null && relatedUserGroups == null) {
-      validateCrowi();
-      const UserGroupRelation = crowi.model('UserGroupRelation');
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 
@@ -390,8 +389,6 @@ export const getPageSchema = (crowi) => {
 
     let relatedUserGroups = userGroups;
     if (user != null && relatedUserGroups == null) {
-      validateCrowi();
-      const UserGroupRelation = crowi.model('UserGroupRelation');
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 
@@ -519,7 +516,6 @@ export const getPageSchema = (crowi) => {
     // determine UserGroup condition
     let userGroups = null;
     if (user != null) {
-      const UserGroupRelation = crowi.model('UserGroupRelation');
       userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 
@@ -535,12 +531,9 @@ export const getPageSchema = (crowi) => {
    * @param {boolean} showAnyoneKnowsLink
    */
   async function addConditionToFilteringByViewerToEdit(builder, user) {
-    validateCrowi();
-
     // determine UserGroup condition
     let userGroups = null;
     if (user != null) {
-      const UserGroupRelation = crowi.model('UserGroupRelation');
       userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 

+ 9 - 4
apps/app/src/server/models/page.ts

@@ -22,6 +22,7 @@ import loggerFactory from '../../utils/logger';
 import { getOrCreateModel } from '../util/mongoose-utils';
 
 import { getPageSchema, extractToAncestorsPaths, populateDataToShowRevision } from './obsolete-page';
+import UserGroupRelation from './user-group-relation';
 
 const { addTrailingSlash, normalizePath } = pathUtils;
 const {
@@ -321,7 +322,6 @@ export class PageQueryBuilder {
     // determine UserGroup condition
     let userGroups;
     if (user != null) {
-      const UserGroupRelation = mongoose.model('UserGroupRelation') as any; // TODO: Typescriptize model
       userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 
@@ -338,7 +338,10 @@ export class PageQueryBuilder {
 
     if (userGroups != null && userGroups.length > 0) {
       grantConditions.push(
-        { grant: GRANT_USER_GROUP, 'grantedGroups.item': { $in: userGroups } },
+        {
+          grant: GRANT_USER_GROUP,
+          grantedGroups: { $elemMatch: { item: { $in: userGroups } } },
+        },
       );
     }
 
@@ -370,7 +373,6 @@ export class PageQueryBuilder {
   async addViewerCondition(user, userGroups = null, includeAnyoneWithTheLink = false): Promise<PageQueryBuilder> {
     let relatedUserGroups = userGroups;
     if (user != null && relatedUserGroups == null) {
-      const UserGroupRelation: any = mongoose.model('UserGroupRelation');
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 
@@ -944,7 +946,10 @@ export function generateGrantCondition(
   }
   else if (userGroups != null && userGroups.length > 0) {
     grantConditions.push(
-      { grant: GRANT_USER_GROUP, 'grantedGroups.item': { $in: userGroups } },
+      {
+        grant: GRANT_USER_GROUP,
+        grantedGroups: { $elemMatch: { item: { $in: userGroups } } },
+      },
     );
   }
 

+ 8 - 2
apps/app/src/server/models/user-group-relation.ts

@@ -20,6 +20,12 @@ export interface UserGroupRelationModel extends Model<UserGroupRelationDocument>
   PAGE_ITEMS: 50,
 
   removeAllByUserGroups: (groupsToDelete: UserGroupDocument[]) => Promise<any>,
+
+  findAllUserIdsForUserGroups: (userGroupIds: ObjectIdLike[]) => Promise<string[]>,
+
+  findGroupsWithDescendantsByGroupAndUser: (group: UserGroupDocument, user) => Promise<UserGroupDocument[]>,
+
+  countByGroupIdAndUser: (userGroupId: string, userData) => Promise<number>
 }
 
 /*
@@ -150,7 +156,7 @@ schema.statics.findAllUserGroupIdsRelatedToUser = async function(user) {
  * @param {User} userData find query param for relatedUser
  * @returns {Promise<number>}
  */
-schema.statics.countByGroupIdAndUser = async function(userGroupId, userData) {
+schema.statics.countByGroupIdAndUser = async function(userGroupId: string, userData): Promise<number> {
   const query = {
     relatedGroup: userGroupId,
     relatedUser: userData.id,
@@ -316,7 +322,7 @@ schema.statics.createByGroupIdsAndUserIds = async function(groupIds, userIds) {
  * @param {UserDocument} user
  * @returns UserGroupDocument[]
  */
-schema.statics.findGroupsWithDescendantsByGroupAndUser = async function(group, user) {
+schema.statics.findGroupsWithDescendantsByGroupAndUser = async function(group: UserGroupDocument, user): Promise<UserGroupDocument[]> {
   const descendantGroups = [group];
 
   const incrementGroupsRecursively = async(groups, user) => {

+ 1 - 1
apps/app/src/server/routes/search.ts

@@ -1,6 +1,7 @@
 import { SupportedAction } from '~/interfaces/activity';
 import loggerFactory from '~/utils/logger';
 
+import UserGroupRelation from '../models/user-group-relation';
 import { isSearchError } from '../models/vo/search-error';
 
 
@@ -129,7 +130,6 @@ module.exports = function(crowi, app) {
 
     let userGroups = [];
     if (user != null) {
-      const UserGroupRelation = crowi.model('UserGroupRelation');
       userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 

+ 18 - 25
apps/app/src/server/service/page-grant.ts

@@ -3,7 +3,6 @@ import {
   pagePathUtils, pathUtils, pageUtils,
   PageGrant, PageGrantCanBeOnTree,
 } from '@growi/core';
-import { et } from 'date-fns/locale';
 import escapeStringRegexp from 'escape-string-regexp';
 import mongoose from 'mongoose';
 
@@ -15,6 +14,7 @@ import UserGroup from '~/server/models/user-group';
 import { includesObjectIds, excludeTestIdsFromTargetIds } from '~/server/util/compare-objectId';
 
 import { ObjectIdLike } from '../interfaces/mongoose-utils';
+import UserGroupRelation from '../models/user-group-relation';
 import { divideByType } from '../util/granted-group';
 
 const { addTrailingSlash } = pathUtils;
@@ -215,7 +215,6 @@ class PageGrantService {
   ): Promise<ComparableTarget> {
     if (includeApplicable) {
       const Page = mongoose.model('Page') as unknown as PageModel;
-      const UserGroupRelation = mongoose.model('UserGroupRelation') as any; // TODO: Typescriptize model
 
       let applicableUserIds: ObjectIdLike[] | undefined;
       let applicableGroupIds: ObjectIdLike[] | undefined;
@@ -234,7 +233,7 @@ class PageGrantService {
 
         const userGroupRelations = await UserGroupRelation.find({ relatedGroup: { $in: targetUserGroups.map(g => g._id) } });
         const externalUserGroupRelations = await ExternalUserGroupRelation.find({ relatedGroup: { $in: targetExternalUserGroups.map(g => g._id) } });
-        applicableUserIds = Array.from(new Set([...userGroupRelations, ...externalUserGroupRelations].map(u => u.relatedUser)));
+        applicableUserIds = Array.from(new Set([...userGroupRelations, ...externalUserGroupRelations].map(u => u.relatedUser as ObjectIdLike)));
 
         const applicableUserGroups = (await Promise.all(targetUserGroups.map((group) => {
           return UserGroup.findGroupsWithDescendantsById(group._id);
@@ -269,7 +268,6 @@ class PageGrantService {
   private async generateComparableAncestor(targetPath: string, includeNotMigratedPages: boolean): Promise<ComparableAncestor> {
     const Page = mongoose.model('Page') as unknown as PageModel;
     const { PageQueryBuilder } = Page;
-    const UserGroupRelation = mongoose.model('UserGroupRelation') as any; // TODO: Typescriptize model
 
     let applicableUserIds: ObjectIdLike[] | undefined;
     let applicableGroupIds: ObjectIdLike[] | undefined;
@@ -297,7 +295,7 @@ class PageGrantService {
 
       const userGroupRelations = await UserGroupRelation.find({ relatedGroup: { $in: grantedUserGroups } }, { _id: 0, relatedUser: 1 });
       const externalUserGroupRelations = await ExternalUserGroupRelation.find({ relatedGroup: { $in: grantedExternalUserGroups } }, { _id: 0, relatedUser: 1 });
-      applicableUserIds = Array.from(new Set([...userGroupRelations, ...externalUserGroupRelations].map(r => r.relatedUser)));
+      applicableUserIds = Array.from(new Set([...userGroupRelations, ...externalUserGroupRelations].map(r => r.relatedUser as ObjectIdLike)));
 
       const applicableUserGroups = (await Promise.all(grantedUserGroups.map((groupId) => {
         return UserGroup.findGroupsWithDescendantsById(groupId);
@@ -324,7 +322,6 @@ class PageGrantService {
    */
   private async generateComparableDescendants(targetPath: string, user, includeNotMigratedPages = false): Promise<ComparableDescendants> {
     const Page = mongoose.model('Page') as unknown as PageModel;
-    const UserGroupRelation = mongoose.model('UserGroupRelation') as any; // TODO: Typescriptize model
 
     // Build conditions
     const $match: {$or: any} = {
@@ -370,21 +367,23 @@ class PageGrantService {
           grantedGroups: 1,
         },
       },
-      { // remove duplicates from pipeline
-        $group: {
-          _id: '$grant',
-          grantedGroupsSet: { $addToSet: '$grantedGroups' },
-          grantedUsersSet: { $addToSet: '$grantedUsers' },
+      {
+        $unwind: { // preprocess for creating groups set
+          path: '$grantedGroups',
+          preserveNullAndEmptyArrays: true,
         },
       },
-      { // flatten granted user set
-        $unwind: {
+      {
+        $unwind: { // preprocess for creating users set
           path: '$grantedUsersSet',
+          preserveNullAndEmptyArrays: true,
         },
       },
-      { // flatten granted group set
-        $unwind: {
-          path: '$grantedGroupsSet',
+      { // remove duplicates from pipeline
+        $group: {
+          _id: '$grant',
+          grantedGroupsSet: { $addToSet: '$grantedGroups' },
+          grantedUsersSet: { $addToSet: '$grantedUsers' },
         },
       },
     ]);
@@ -473,7 +472,6 @@ class PageGrantService {
 
   async calcApplicableGrantData(page, user): Promise<IRecordApplicableGrant> {
     const Page = mongoose.model('Page') as unknown as PageModel;
-    const UserGroupRelation = mongoose.model('UserGroupRelation') as any; // TODO: Typescriptize model
 
     // -- Public only if top page
     const isOnlyPublicApplicable = isTopPage(page.path);
@@ -538,10 +536,10 @@ class PageGrantService {
       const applicableGroups = [...applicableUserGroups, ...applicableExternalUserGroups];
 
       const isUserExistInUserGroup = (await Promise.all(targetUserGroups.map((group) => {
-        return UserGroupRelation.countByGroupIdAndUser(group, user);
+        return UserGroupRelation.countByGroupIdAndUser(group._id, user);
       }))).some(count => count > 0);
       const isUserExistInExternalUserGroup = (await Promise.all(targetExternalUserGroups.map((group) => {
-        return ExternalUserGroupRelation.countByGroupIdAndUser(group, user);
+        return ExternalUserGroupRelation.countByGroupIdAndUser(group._id, user);
       }))).some(count => count > 0);
       const isUserExistInGroup = isUserExistInUserGroup || isUserExistInExternalUserGroup;
 
@@ -562,9 +560,7 @@ class PageGrantService {
    * @returns {Promise<boolean>}
    */
   async canOverwriteDescendants(targetPath: string, operator: { _id: ObjectIdLike }, updateGrantInfo: UpdateGrantInfo): Promise<boolean> {
-    const UserGroupRelationModel = mongoose.model('UserGroupRelation') as any; // TODO: TypeScriptize model
-
-    const relatedGroupIds = await UserGroupRelationModel.findAllUserGroupIdsRelatedToUser(operator);
+    const relatedGroupIds = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(operator);
     const operatorGrantInfo = {
       userId: operator._id,
       userGroupIds: new Set<ObjectIdLike>(relatedGroupIds),
@@ -613,7 +609,6 @@ class PageGrantService {
       if (grantGroupIds == null) {
         throw Error('The parameter `grantGroupIds` is required.');
       }
-      const UserGroupRelation = mongoose.model('UserGroupRelation') as any; // TODO: Typescriptize model
       const { grantedUserGroups: grantedUserGroupIds, grantedExternalUserGroups: grantedExternalUserGroupIds } = divideByType(grantGroupIds);
 
       const userGroupUserIds = await UserGroupRelation.findAllUserIdsForUserGroups(grantedUserGroupIds);
@@ -658,7 +653,6 @@ class PageGrantService {
       const isNonApplicableGroupExist = excludeTestIdsFromTargetIds(
         [...descendantPagesGrantInfo.grantedUserGroupIds], [...operatorGrantInfo.userGroupIds],
       ).length > 0;
-
       if (isNonApplicableGroupExist) {
         return false;
       }
@@ -696,7 +690,6 @@ class PageGrantService {
       const isUpdateGroupUsersIncludeAllDescendantsOwners = excludeTestIdsFromTargetIds(
         [...descendantPagesGrantInfo.grantedUserIds], [...updateGrantInfo.grantedUserGroupInfo.userIds],
       ).length === 0; // b.
-
       return isAllDescendantGroupsChildrenOrItselfOfUpdateGroup && isUpdateGroupUsersIncludeAllDescendantsOwners;
     }
 

+ 2 - 5
apps/app/src/server/service/page.ts

@@ -36,6 +36,7 @@ import PageOperation, { PageOperationDocument } from '../models/page-operation';
 import { PageRedirectModel } from '../models/page-redirect';
 import { serializePageSecurely } from '../models/serializers/page-serializer';
 import Subscription from '../models/subscription';
+import UserGroupRelation from '../models/user-group-relation';
 import { V5ConversionError } from '../models/vo/v5-conversion-error';
 
 const debug = require('debug')('growi:services:page');
@@ -2338,7 +2339,6 @@ class PageService {
     // aggregation options
     let userGroups;
     if (user != null && userGroups == null) {
-      const UserGroupRelation = mongoose.model('UserGroupRelation') as any; // Typescriptize model
       userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
     const viewerCondition = Page.generateGrantCondition(user, userGroups);
@@ -2889,7 +2889,6 @@ class PageService {
     // determine UserGroup condition
     let userGroups = null;
     if (user != null) {
-      const UserGroupRelation = mongoose.model('UserGroupRelation') as any; // TODO: Typescriptize model
       userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
 
@@ -3292,7 +3291,6 @@ class PageService {
     const pipeline = this.buildBasePipelineToCreateEmptyPages(paths, onlyMigratedAsExistingPages, andFilter);
     let userGroups = null;
     if (user != null) {
-      const UserGroupRelation = mongoose.model('UserGroupRelation') as any;
       userGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
     const grantCondition = Page.generateGrantCondition(user, userGroups);
@@ -3446,7 +3444,6 @@ class PageService {
     }
 
     if (grant === PageGrant.GRANT_USER_GROUP) {
-      const UserGroupRelation = mongoose.model('UserGroupRelation') as any;
       const count = await UserGroupRelation.countByGroupIdAndUser(grantUserGroupId, user);
 
       if (count === 0) {
@@ -3867,7 +3864,7 @@ class PageService {
     const newPageData = pageData;
 
     const grant = options.grant ?? clonedPageData.grant; // use the previous data if absence
-    const grantUserGroupIds: undefined | GrantedGroup[] = options.grantUserGroupIds ?? clonedPageData.grantedGroups;
+    const grantUserGroupIds = options.grantUserGroupIds ?? clonedPageData.grantedGroups;
 
     const grantedUserIds = clonedPageData.grantedUserIds || [user._id];
     const shouldBeOnTree = grant !== PageGrant.GRANT_RESTRICTED;