diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/server/follows.ts | 35 | ||||
-rw-r--r-- | server/helpers/custom-validators/follows.ts | 14 | ||||
-rw-r--r-- | server/middlewares/validators/follows.ts | 18 | ||||
-rw-r--r-- | server/models/activitypub/actor-follow.ts | 28 | ||||
-rw-r--r-- | server/tests/api/check-params/follows.ts | 44 | ||||
-rw-r--r-- | server/tests/api/server/follows.ts | 26 |
6 files changed, 141 insertions, 24 deletions
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index 37647622b..e7fd3aabd 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts | |||
@@ -19,7 +19,8 @@ import { | |||
19 | followingSortValidator, | 19 | followingSortValidator, |
20 | followValidator, | 20 | followValidator, |
21 | getFollowerValidator, | 21 | getFollowerValidator, |
22 | removeFollowingValidator | 22 | removeFollowingValidator, |
23 | listFollowsValidator | ||
23 | } from '../../../middlewares/validators' | 24 | } from '../../../middlewares/validators' |
24 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 25 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
25 | import { JobQueue } from '../../../lib/job-queue' | 26 | import { JobQueue } from '../../../lib/job-queue' |
@@ -29,6 +30,7 @@ import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' | |||
29 | 30 | ||
30 | const serverFollowsRouter = express.Router() | 31 | const serverFollowsRouter = express.Router() |
31 | serverFollowsRouter.get('/following', | 32 | serverFollowsRouter.get('/following', |
33 | listFollowsValidator, | ||
32 | paginationValidator, | 34 | paginationValidator, |
33 | followingSortValidator, | 35 | followingSortValidator, |
34 | setDefaultSort, | 36 | setDefaultSort, |
@@ -52,6 +54,7 @@ serverFollowsRouter.delete('/following/:host', | |||
52 | ) | 54 | ) |
53 | 55 | ||
54 | serverFollowsRouter.get('/followers', | 56 | serverFollowsRouter.get('/followers', |
57 | listFollowsValidator, | ||
55 | paginationValidator, | 58 | paginationValidator, |
56 | followersSortValidator, | 59 | followersSortValidator, |
57 | setDefaultSort, | 60 | setDefaultSort, |
@@ -92,26 +95,28 @@ export { | |||
92 | 95 | ||
93 | async function listFollowing (req: express.Request, res: express.Response) { | 96 | async function listFollowing (req: express.Request, res: express.Response) { |
94 | const serverActor = await getServerActor() | 97 | const serverActor = await getServerActor() |
95 | const resultList = await ActorFollowModel.listFollowingForApi( | 98 | const resultList = await ActorFollowModel.listFollowingForApi({ |
96 | serverActor.id, | 99 | id: serverActor.id, |
97 | req.query.start, | 100 | start: req.query.start, |
98 | req.query.count, | 101 | count: req.query.count, |
99 | req.query.sort, | 102 | sort: req.query.sort, |
100 | req.query.search | 103 | search: req.query.search, |
101 | ) | 104 | state: req.query.state |
105 | }) | ||
102 | 106 | ||
103 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 107 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
104 | } | 108 | } |
105 | 109 | ||
106 | async function listFollowers (req: express.Request, res: express.Response) { | 110 | async function listFollowers (req: express.Request, res: express.Response) { |
107 | const serverActor = await getServerActor() | 111 | const serverActor = await getServerActor() |
108 | const resultList = await ActorFollowModel.listFollowersForApi( | 112 | const resultList = await ActorFollowModel.listFollowersForApi({ |
109 | serverActor.id, | 113 | actorId: serverActor.id, |
110 | req.query.start, | 114 | start: req.query.start, |
111 | req.query.count, | 115 | count: req.query.count, |
112 | req.query.sort, | 116 | sort: req.query.sort, |
113 | req.query.search | 117 | search: req.query.search, |
114 | ) | 118 | state: req.query.state |
119 | }) | ||
115 | 120 | ||
116 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 121 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
117 | } | 122 | } |
diff --git a/server/helpers/custom-validators/follows.ts b/server/helpers/custom-validators/follows.ts new file mode 100644 index 000000000..fbef7ad87 --- /dev/null +++ b/server/helpers/custom-validators/follows.ts | |||
@@ -0,0 +1,14 @@ | |||
1 | import { exists } from './misc' | ||
2 | import { FollowState } from '@shared/models' | ||
3 | |||
4 | function isFollowStateValid (value: FollowState) { | ||
5 | if (!exists(value)) return false | ||
6 | |||
7 | return value === 'pending' || value === 'accepted' | ||
8 | } | ||
9 | |||
10 | // --------------------------------------------------------------------------- | ||
11 | |||
12 | export { | ||
13 | isFollowStateValid | ||
14 | } | ||
diff --git a/server/middlewares/validators/follows.ts b/server/middlewares/validators/follows.ts index 788735663..454f9f2b8 100644 --- a/server/middlewares/validators/follows.ts +++ b/server/middlewares/validators/follows.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { body, param } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { isTestInstance } from '../../helpers/core-utils' | 3 | import { isTestInstance } from '../../helpers/core-utils' |
4 | import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' | 4 | import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' |
5 | import { logger } from '../../helpers/logger' | 5 | import { logger } from '../../helpers/logger' |
@@ -11,6 +11,19 @@ import { ActorModel } from '../../models/activitypub/actor' | |||
11 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' | 11 | import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' |
12 | import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' | 12 | import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' |
13 | import { MActorFollowActorsDefault } from '@server/typings/models' | 13 | import { MActorFollowActorsDefault } from '@server/typings/models' |
14 | import { isFollowStateValid } from '@server/helpers/custom-validators/follows' | ||
15 | |||
16 | const listFollowsValidator = [ | ||
17 | query('state') | ||
18 | .optional() | ||
19 | .custom(isFollowStateValid).withMessage('Should have a valid follow state'), | ||
20 | |||
21 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
22 | if (areValidationErrors(req, res)) return | ||
23 | |||
24 | return next() | ||
25 | } | ||
26 | ] | ||
14 | 27 | ||
15 | const followValidator = [ | 28 | const followValidator = [ |
16 | body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), | 29 | body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), |
@@ -110,5 +123,6 @@ export { | |||
110 | followValidator, | 123 | followValidator, |
111 | removeFollowingValidator, | 124 | removeFollowingValidator, |
112 | getFollowerValidator, | 125 | getFollowerValidator, |
113 | acceptOrRejectFollowerValidator | 126 | acceptOrRejectFollowerValidator, |
127 | listFollowsValidator | ||
114 | } | 128 | } |
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts index 24272a40e..09bc6853d 100644 --- a/server/models/activitypub/actor-follow.ts +++ b/server/models/activitypub/actor-follow.ts | |||
@@ -292,12 +292,24 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
292 | return ActorFollowModel.findAll(query) | 292 | return ActorFollowModel.findAll(query) |
293 | } | 293 | } |
294 | 294 | ||
295 | static listFollowingForApi (id: number, start: number, count: number, sort: string, search?: string) { | 295 | static listFollowingForApi (options: { |
296 | id: number, | ||
297 | start: number, | ||
298 | count: number, | ||
299 | sort: string, | ||
300 | state?: FollowState, | ||
301 | search?: string | ||
302 | }) { | ||
303 | const { id, start, count, sort, search, state } = options | ||
304 | |||
305 | const followWhere = state ? { state } : {} | ||
306 | |||
296 | const query = { | 307 | const query = { |
297 | distinct: true, | 308 | distinct: true, |
298 | offset: start, | 309 | offset: start, |
299 | limit: count, | 310 | limit: count, |
300 | order: getSort(sort), | 311 | order: getSort(sort), |
312 | where: followWhere, | ||
301 | include: [ | 313 | include: [ |
302 | { | 314 | { |
303 | model: ActorModel, | 315 | model: ActorModel, |
@@ -335,12 +347,24 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
335 | }) | 347 | }) |
336 | } | 348 | } |
337 | 349 | ||
338 | static listFollowersForApi (actorId: number, start: number, count: number, sort: string, search?: string) { | 350 | static listFollowersForApi (options: { |
351 | actorId: number, | ||
352 | start: number, | ||
353 | count: number, | ||
354 | sort: string, | ||
355 | state?: FollowState, | ||
356 | search?: string | ||
357 | }) { | ||
358 | const { actorId, start, count, sort, search, state } = options | ||
359 | |||
360 | const followWhere = state ? { state } : {} | ||
361 | |||
339 | const query = { | 362 | const query = { |
340 | distinct: true, | 363 | distinct: true, |
341 | offset: start, | 364 | offset: start, |
342 | limit: count, | 365 | limit: count, |
343 | order: getSort(sort), | 366 | order: getSort(sort), |
367 | where: followWhere, | ||
344 | include: [ | 368 | include: [ |
345 | { | 369 | { |
346 | model: ActorModel, | 370 | model: ActorModel, |
diff --git a/server/tests/api/check-params/follows.ts b/server/tests/api/check-params/follows.ts index 2eb54cb0a..488666a75 100644 --- a/server/tests/api/check-params/follows.ts +++ b/server/tests/api/check-params/follows.ts | |||
@@ -6,7 +6,7 @@ import { | |||
6 | cleanupTests, | 6 | cleanupTests, |
7 | createUser, | 7 | createUser, |
8 | flushAndRunServer, | 8 | flushAndRunServer, |
9 | makeDeleteRequest, | 9 | makeDeleteRequest, makeGetRequest, |
10 | makePostBodyRequest, | 10 | makePostBodyRequest, |
11 | ServerInfo, | 11 | ServerInfo, |
12 | setAccessTokensToServers, | 12 | setAccessTokensToServers, |
@@ -131,6 +131,27 @@ describe('Test server follows API validators', function () { | |||
131 | it('Should fail with an incorrect sort', async function () { | 131 | it('Should fail with an incorrect sort', async function () { |
132 | await checkBadSortPagination(server.url, path) | 132 | await checkBadSortPagination(server.url, path) |
133 | }) | 133 | }) |
134 | |||
135 | it('Should fail with an incorrect state', async function () { | ||
136 | await makeGetRequest({ | ||
137 | url: server.url, | ||
138 | path, | ||
139 | query: { | ||
140 | state: 'blabla' | ||
141 | } | ||
142 | }) | ||
143 | }) | ||
144 | |||
145 | it('Should fail succeed with the correct params', async function () { | ||
146 | await makeGetRequest({ | ||
147 | url: server.url, | ||
148 | path, | ||
149 | statusCodeExpected: 200, | ||
150 | query: { | ||
151 | state: 'accepted' | ||
152 | } | ||
153 | }) | ||
154 | }) | ||
134 | }) | 155 | }) |
135 | 156 | ||
136 | describe('When listing followers', function () { | 157 | describe('When listing followers', function () { |
@@ -147,6 +168,27 @@ describe('Test server follows API validators', function () { | |||
147 | it('Should fail with an incorrect sort', async function () { | 168 | it('Should fail with an incorrect sort', async function () { |
148 | await checkBadSortPagination(server.url, path) | 169 | await checkBadSortPagination(server.url, path) |
149 | }) | 170 | }) |
171 | |||
172 | it('Should fail with an incorrect state', async function () { | ||
173 | await makeGetRequest({ | ||
174 | url: server.url, | ||
175 | path, | ||
176 | query: { | ||
177 | state: 'blabla' | ||
178 | } | ||
179 | }) | ||
180 | }) | ||
181 | |||
182 | it('Should fail succeed with the correct params', async function () { | ||
183 | await makeGetRequest({ | ||
184 | url: server.url, | ||
185 | path, | ||
186 | statusCodeExpected: 200, | ||
187 | query: { | ||
188 | state: 'accepted' | ||
189 | } | ||
190 | }) | ||
191 | }) | ||
150 | }) | 192 | }) |
151 | 193 | ||
152 | describe('When removing a follower', function () { | 194 | describe('When removing a follower', function () { |
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts index e8d6f5138..36a061926 100644 --- a/server/tests/api/server/follows.ts +++ b/server/tests/api/server/follows.ts | |||
@@ -97,14 +97,23 @@ describe('Test follows', function () { | |||
97 | expect(server3Follow.state).to.equal('accepted') | 97 | expect(server3Follow.state).to.equal('accepted') |
98 | }) | 98 | }) |
99 | 99 | ||
100 | it('Should search followings on server 1', async function () { | 100 | it('Should search/filter followings on server 1', async function () { |
101 | { | 101 | { |
102 | const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', ':' + servers[1].port) | 102 | const search = ':' + servers[1].port |
103 | const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', search) | ||
103 | const follows = res.body.data | 104 | const follows = res.body.data |
104 | 105 | ||
105 | expect(res.body.total).to.equal(1) | 106 | expect(res.body.total).to.equal(1) |
106 | expect(follows.length).to.equal(1) | 107 | expect(follows.length).to.equal(1) |
107 | expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[1].port) | 108 | expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[1].port) |
109 | |||
110 | const res2 = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', search, 'accepted') | ||
111 | expect(res2.body.total).to.equal(1) | ||
112 | expect(res2.body.data).to.have.lengthOf(1) | ||
113 | |||
114 | const res3 = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', search, 'pending') | ||
115 | expect(res3.body.total).to.equal(0) | ||
116 | expect(res3.body.data).to.have.lengthOf(0) | ||
108 | } | 117 | } |
109 | 118 | ||
110 | { | 119 | { |
@@ -139,14 +148,23 @@ describe('Test follows', function () { | |||
139 | } | 148 | } |
140 | }) | 149 | }) |
141 | 150 | ||
142 | it('Should search followers on server 2', async function () { | 151 | it('Should search/filter followers on server 2', async function () { |
143 | { | 152 | { |
144 | const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', servers[0].port + '') | 153 | const search = servers[0].port + '' |
154 | const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', search) | ||
145 | const follows = res.body.data | 155 | const follows = res.body.data |
146 | 156 | ||
147 | expect(res.body.total).to.equal(1) | 157 | expect(res.body.total).to.equal(1) |
148 | expect(follows.length).to.equal(1) | 158 | expect(follows.length).to.equal(1) |
149 | expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[2].port) | 159 | expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[2].port) |
160 | |||
161 | const res2 = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', search, 'accepted') | ||
162 | expect(res2.body.total).to.equal(1) | ||
163 | expect(res2.body.data).to.have.lengthOf(1) | ||
164 | |||
165 | const res3 = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', search, 'pending') | ||
166 | expect(res3.body.total).to.equal(0) | ||
167 | expect(res3.body.data).to.have.lengthOf(0) | ||
150 | } | 168 | } |
151 | 169 | ||
152 | { | 170 | { |