From 9f9eb947094a2221af7640b61e9eb4171db3f5e1 Mon Sep 17 00:00:00 2001
From: Geoffroy Empain <geoffroy@charlie-bravo.be>
Date: Mon, 28 Dec 2020 12:50:48 +0100
Subject: [PATCH] fix: search is broken

---
 src/entities/api/api-scope.ts                 | 43 +++++-----
 .../orgs/handlers/sites/list-sites.ts         | 86 +++++++++++++++++++
 src/entities/orgs/routes.ts                   | 12 +++
 .../{list-sites.ts => list-team-sites.ts}     |  2 +-
 src/entities/teams/routes.ts                  |  4 +-
 src/utils/getPagination.ts                    |  5 +-
 6 files changed, 126 insertions(+), 26 deletions(-)
 create mode 100644 src/entities/orgs/handlers/sites/list-sites.ts
 rename src/entities/teams/handlers/sites/{list-sites.ts => list-team-sites.ts} (98%)

diff --git a/src/entities/api/api-scope.ts b/src/entities/api/api-scope.ts
index 53a35c5..1d76c2f 100644
--- a/src/entities/api/api-scope.ts
+++ b/src/entities/api/api-scope.ts
@@ -1,20 +1,20 @@
 export enum ApiScope {
   user_read = 'user.read',
   user_disconnect = 'user.disconnect',
-  user_hook_list = 'user_hook_list',
-  user_hook_create = 'user_hook_create',
-  user_hook_read = 'user_hook_read',
-  user_hook_update = 'user_hook_update',
-  user_hook_delete = 'user_hook_delete',
+  user_hook_list = 'user.hook.list',
+  user_hook_create = 'user.hook.create',
+  user_hook_read = 'user.hook.read',
+  user_hook_update = 'user.hook.update',
+  user_hook_delete = 'user.hook.delete',
   org_create = 'org.create',
   org_read = 'org.read',
   org_list = 'org.list',
   org_update = 'org.update',
-  org_hook_list = 'org_hook_list',
-  org_hook_create = 'org_hook_create',
-  org_hook_read = 'org_hook_read',
-  org_hook_update = 'org_hook_update',
-  org_hook_delete = 'org_hook_delete',
+  org_hook_list = 'org.hook.list',
+  org_hook_create = 'org.hook.create',
+  org_hook_read = 'org.hook.read',
+  org_hook_update = 'org.hook.update',
+  org_hook_delete = 'org.hook.delete',
   member_get_current = 'member.get.current',
   members_list = 'members.list',
   member_delete = 'member.delete',
@@ -35,14 +35,15 @@ export enum ApiScope {
   team_member_delete = 'team.member.delete',
   team_sites_list = 'team.sites.list',
   team_sites_add = 'team.sites.add',
-  team_hook_list = 'team_hook_list',
-  team_hook_create = 'team_hook_create',
-  team_hook_read = 'team_hook_read',
-  team_hook_update = 'team_hook_update',
-  team_hook_delete = 'team_hook_delete',
-  site_delete = 'site.delete',
+  team_hook_list = 'team.hook.list',
+  team_hook_create = 'team.hook.create',
+  team_hook_read = 'team.hook.read',
+  team_hook_update = 'team.hook.update',
+  team_hook_delete = 'team.hook.delete',
+  sites_list = 'sites.list',
   site_read = 'site.read',
   site_update = 'site.update',
+  site_delete = 'site.delete',
   site_password_set = 'site.password.set',
   site_password_remove = 'site.password.remove',
   site_name_validate = 'site.name.validate',
@@ -57,11 +58,11 @@ export enum ApiScope {
   site_branch_password_remove = 'site.branch.password.remove',
   site_branch_redirects_read = 'site.branch.redirects.read',
   site_branch_redirects_set = 'site.branch.redirects.set',
-  site_hook_list = 'site_hook_list',
-  site_hook_create = 'site_hook_create',
-  site_hook_read = 'site_hook_read',
-  site_hook_update = 'site_hook_update',
-  site_hook_delete = 'site_hook_delete',
+  site_hook_list = 'site.hook.list',
+  site_hook_create = 'site.hook.create',
+  site_hook_read = 'site.hook.read',
+  site_hook_update = 'site.hook.update',
+  site_hook_delete = 'site.hook.delete',
   site_releases_list = 'site.releases.list',
   release_upload = 'release.upload',
   release_read = 'release.read',
diff --git a/src/entities/orgs/handlers/sites/list-sites.ts b/src/entities/orgs/handlers/sites/list-sites.ts
new file mode 100644
index 0000000..0821c29
--- /dev/null
+++ b/src/entities/orgs/handlers/sites/list-sites.ts
@@ -0,0 +1,86 @@
+import { Request, Response } from 'express';
+import { Site, Sites } from '../../../sites/site';
+import { object, string } from 'joi';
+import { wrapAsyncMiddleware } from '../../../../commons/utils/wrap-async-middleware';
+import { getPagination, pageResponse, pageValidators } from '../../../../utils/getPagination';
+import { FilterQuery } from 'mongodb';
+import { query } from '../../../../commons/express-joi/query';
+import { Teams } from '../../../teams/team';
+import { getUser } from '../../../../auth/utils/get-user';
+import { params } from '../../../../commons/express-joi/params';
+import { $id } from '../../../../utils/id';
+import { Members } from '../../../members/member';
+import { isOrgMemberGuard } from '../../guards/is-org-member-guard';
+import { orgExistsGuard } from '../../guards/org-exists-guard';
+import { isAdminOrOwner } from '../../../../auth/guards/is-admin-or-owner';
+
+const validators = [
+  ...pageValidators,
+  params(object({
+    orgId: $id,
+  })),
+  query({
+    search: {
+      $schema: string().optional().empty('').min(3)
+        .max(255),
+    },
+  }),
+];
+
+async function handler(req: Request, res: Response): Promise<void> {
+  const { orgId } = req.params;
+  const { search } = req.query;
+  const pagination = getPagination(req);
+
+  // find user teams in this org
+  const user = getUser(req);
+  const adminOrOwner = await isAdminOrOwner(user._id, orgId);
+  const member = await Members().findOne({
+    orgId,
+    userId: user._id,
+  });
+
+  const teams = await Teams()
+    .find(adminOrOwner ? {} : {
+      members: member._id,
+    })
+    .project({ _id: 1 })
+    .toArray();
+
+  const dbQuery: FilterQuery<Site> = {
+    teamId: {
+      $in: teams.map(({ _id }) => _id),
+    },
+    ...(
+      search
+        ? {
+          $text: {
+            $search: search,
+          },
+        }
+        : undefined as any
+    ),
+  };
+
+  const count = await Sites()
+    .find(dbQuery)
+    .count();
+
+  const sites = await Sites()
+    .find(dbQuery)
+    .sort({
+      updatedAt: -1,
+    })
+    .skip(pagination.offset)
+    .limit(pagination.size)
+    .toArray();
+
+  res.json(pageResponse(sites, count));
+}
+
+export const listSites = [
+  ...orgExistsGuard,
+  ...isOrgMemberGuard,
+  ...validators,
+  wrapAsyncMiddleware(handler),
+];
diff --git a/src/entities/orgs/routes.ts b/src/entities/orgs/routes.ts
index ad4b63b..4012c6e 100644
--- a/src/entities/orgs/routes.ts
+++ b/src/entities/orgs/routes.ts
@@ -12,6 +12,7 @@ import { deleteInvite } from './handlers/invites/delete-invite';
 import { createOrg } from './handlers/create-org';
 import { apiEndpoint } from '../api/api-endpoint';
 import { ApiScope } from '../api/api-scope';
+import { listSites } from './handlers/sites/list-sites';
 
 const router = Router();
 
@@ -123,4 +124,15 @@ apiEndpoint({
   router,
 });
 
+// sites
+apiEndpoint({
+  name: 'list sites',
+  method: 'get',
+  path: '/api/v1/orgs/:orgId/sites',
+  handler: listSites,
+  auth: true,
+  apiScope: ApiScope.sites_list,
+  router,
+});
+
 export default router;
diff --git a/src/entities/teams/handlers/sites/list-sites.ts b/src/entities/teams/handlers/sites/list-team-sites.ts
similarity index 98%
rename from src/entities/teams/handlers/sites/list-sites.ts
rename to src/entities/teams/handlers/sites/list-team-sites.ts
index 4441ebf..dbef7db 100644
--- a/src/entities/teams/handlers/sites/list-sites.ts
+++ b/src/entities/teams/handlers/sites/list-team-sites.ts
@@ -57,7 +57,7 @@ async function handler(req: Request, res: Response): Promise<void> {
   res.json(pageResponse(sites, count));
 }
 
-export const listSites = [
+export const listTeamSites = [
   ...teamExistsGuard,
   ...canReadTeamGuard,
   ...validators,
diff --git a/src/entities/teams/routes.ts b/src/entities/teams/routes.ts
index dac1964..e58aed8 100644
--- a/src/entities/teams/routes.ts
+++ b/src/entities/teams/routes.ts
@@ -4,7 +4,7 @@ import { deleteTeam } from './handlers/delete-team';
 import { getTeam } from './handlers/get-team';
 import { addMember } from './handlers/members/add-member';
 import { deleteMember } from './handlers/members/delete-member';
-import { listSites } from './handlers/sites/list-sites';
+import { listTeamSites } from './handlers/sites/list-team-sites';
 import { listMembers } from './handlers/members/list-members';
 import { addSite } from './handlers/sites/add-site';
 import { apiEndpoint } from '../api/api-endpoint';
@@ -46,7 +46,7 @@ apiEndpoint({
   name: 'list sites',
   method: 'get',
   path: '/api/v1/teams/:teamId/sites',
-  handler: listSites,
+  handler: listTeamSites,
   auth: true,
   apiScope: ApiScope.team_sites_list,
   router,
diff --git a/src/utils/getPagination.ts b/src/utils/getPagination.ts
index 5160d7b..49beb16 100644
--- a/src/utils/getPagination.ts
+++ b/src/utils/getPagination.ts
@@ -7,11 +7,12 @@ export const pageValidators = [
   query({
     size: {
       transform: stringToInt(),
-      $schema: number().default(10).min(0).max(100),
+      $schema: number().optional().default(10).min(0)
+        .max(100),
     },
     page: {
       transform: stringToInt(),
-      $schema: number().default(0).min(0),
+      $schema: number().optional().default(0).min(0),
     },
   }),
 ];