Преглед изворни кода

Merge pull request #4612 from weseek/feat/api-get-children

feat: Api get children
Haku Mizuki пре 4 година
родитељ
комит
50ed9bb261

+ 34 - 6
packages/app/src/server/models/page.ts

@@ -39,6 +39,7 @@ export interface PageModel extends Model<PageDocument> {
   findByPathAndViewer(path: string | null, user, userGroups?, useFindOne?): Promise<PageDocument[]>
   findSiblingsByPathAndViewer(path: string | null, user, userGroups?): Promise<PageDocument[]>
   findAncestorsByPathOrId(pathOrId: string): Promise<PageDocument[]>
+  findChildrenByParentPathOrIdAndViewer(parentPathOrId: string, user, userGroups?): Promise<PageDocument[]>
 }
 
 const ObjectId = mongoose.Schema.Types.ObjectId;
@@ -94,6 +95,19 @@ const hasSlash = (str: string): boolean => {
   return str.includes('/');
 };
 
+/*
+ * Generate RE2 instance for one level lower path
+ */
+const generateChildrenRegExp = (path: string): RE2 => {
+  // https://regex101.com/r/iu1vYF/1
+  // ex. / OR /any_level1
+  if (isTopPage(path)) return new RE2(/^\/[^\\/]*$/);
+
+  // https://regex101.com/r/mrDJrx/1
+  // ex. /parent/any_child OR /any_level1
+  return new RE2(`^${path}(\\/[^/]+)\\/?$`);
+};
+
 /*
  * Create empty pages if the page in paths didn't exist
  */
@@ -214,12 +228,7 @@ schema.statics.findSiblingsByPathAndViewer = async function(path: string | null,
   const _parentPath = nodePath.dirname(path);
   const parentPath = isTopPage(_parentPath) ? '' : _parentPath;
 
-  // https://regex101.com/r/mrDJrx/1
-  // ex. /parent/any_child OR /any_level1
-  let regexp = new RE2(`^${parentPath}(\\/[^/]+)\\/?$`);
-  // https://regex101.com/r/iu1vYF/1
-  // ex. / OR /any_level1
-  if (isTopPage(path)) regexp = new RE2(/^\/[^\\/]*$/);
+  const regexp = generateChildrenRegExp(parentPath);
 
   const queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp.source, $options: regexp.flags } }));
   await addViewerCondition(queryBuilder, user, userGroups);
@@ -262,6 +271,25 @@ schema.statics.findAncestorsByPathOrId = async function(pathOrId: string): Promi
   return ancestors;
 };
 
+/*
+ * Find all children by parent's path or id. Using id should be prioritized
+ */
+schema.statics.findChildrenByParentPathOrIdAndViewer = async function(parentPathOrId: string, user, userGroups = null): Promise<PageDocument[]> {
+  let queryBuilder: PageQueryBuilder;
+  if (hasSlash(parentPathOrId)) {
+    const path = parentPathOrId;
+    const regexp = generateChildrenRegExp(path);
+    queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp.source } }));
+  }
+  else {
+    const parentId = parentPathOrId;
+    queryBuilder = new PageQueryBuilder(this.find({ parent: parentId }));
+  }
+  await addViewerCondition(queryBuilder, user, userGroups);
+
+  return queryBuilder.query.lean().exec();
+};
+
 
 /*
  * Merge obsolete page model methods and define new methods which depend on crowi instance

+ 22 - 0
packages/app/src/server/routes/apiv3/page-listing.ts

@@ -73,6 +73,9 @@ export default (crowi: Crowi): Router => {
     return res.apiv3({ target, siblings });
   });
 
+  /*
+   * In most cases, using path should be prioritized
+   */
   // eslint-disable-next-line max-len
   router.get('/ancestors', accessTokenParser, loginRequiredStrictly, validator.pageIdOrPathRequired, apiV3FormValidator, async(req: AuthorizedRequest, res: ApiV3Response): Promise<any> => {
     const { id, path } = req.query;
@@ -95,5 +98,24 @@ export default (crowi: Crowi): Router => {
     return res.apiv3({ ancestors });
   });
 
+  /*
+   * In most cases, using id should be prioritized
+   */
+  // eslint-disable-next-line max-len
+  router.get('/children', accessTokenParser, loginRequiredStrictly, validator.pageIdOrPathRequired, async(req: AuthorizedRequest, res: ApiV3Response) => {
+    const { id, path } = req.query;
+
+    const Page: PageModel = crowi.model('Page');
+
+    try {
+      const pages = await Page.findChildrenByParentPathOrIdAndViewer((id || path)as string, req.user);
+      return res.apiv3({ pages });
+    }
+    catch (err) {
+      logger.error('Error occurred while finding children.', err);
+      return res.apiv3Err(new ErrorV3('Error occurred while finding children.'));
+    }
+  });
+
   return router;
 };