aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+about/about-follows/about-follows.component.ts4
-rw-r--r--client/src/app/+admin/follows/followers-list/followers-list.component.ts2
-rw-r--r--client/src/app/+admin/follows/following-list/following-list.component.ts2
-rw-r--r--client/src/app/shared/instance/follow.service.ts22
-rw-r--r--server/controllers/api/server/follows.ts35
-rw-r--r--server/helpers/custom-validators/follows.ts14
-rw-r--r--server/middlewares/validators/follows.ts18
-rw-r--r--server/models/activitypub/actor-follow.ts28
-rw-r--r--server/tests/api/check-params/follows.ts44
-rw-r--r--server/tests/api/server/follows.ts26
-rw-r--r--shared/extra-utils/server/follows.ts11
11 files changed, 171 insertions, 35 deletions
diff --git a/client/src/app/+about/about-follows/about-follows.component.ts b/client/src/app/+about/about-follows/about-follows.component.ts
index d60307928..fc265fecb 100644
--- a/client/src/app/+about/about-follows/about-follows.component.ts
+++ b/client/src/app/+about/about-follows/about-follows.component.ts
@@ -74,7 +74,7 @@ export class AboutFollowsComponent implements OnInit {
74 private loadMoreFollowers () { 74 private loadMoreFollowers () {
75 const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination) 75 const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination)
76 76
77 this.followService.getFollowers(pagination, this.sort) 77 this.followService.getFollowers({ pagination: pagination, sort: this.sort, state: 'accepted' })
78 .subscribe( 78 .subscribe(
79 resultList => { 79 resultList => {
80 const newFollowers = resultList.data.map(r => r.follower.host) 80 const newFollowers = resultList.data.map(r => r.follower.host)
@@ -92,7 +92,7 @@ export class AboutFollowsComponent implements OnInit {
92 private loadMoreFollowings () { 92 private loadMoreFollowings () {
93 const pagination = this.restService.componentPaginationToRestPagination(this.followingsPagination) 93 const pagination = this.restService.componentPaginationToRestPagination(this.followingsPagination)
94 94
95 this.followService.getFollowing(pagination, this.sort) 95 this.followService.getFollowing({ pagination, sort: this.sort, state: 'accepted' })
96 .subscribe( 96 .subscribe(
97 resultList => { 97 resultList => {
98 const newFollowings = resultList.data.map(r => r.following.host) 98 const newFollowings = resultList.data.map(r => r.following.host)
diff --git a/client/src/app/+admin/follows/followers-list/followers-list.component.ts b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
index eb3137e26..707daef84 100644
--- a/client/src/app/+admin/follows/followers-list/followers-list.component.ts
+++ b/client/src/app/+admin/follows/followers-list/followers-list.component.ts
@@ -88,7 +88,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
88 } 88 }
89 89
90 protected loadData () { 90 protected loadData () {
91 this.followService.getFollowers(this.pagination, this.sort, this.search) 91 this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
92 .subscribe( 92 .subscribe(
93 resultList => { 93 resultList => {
94 this.followers = resultList.data 94 this.followers = resultList.data
diff --git a/client/src/app/+admin/follows/following-list/following-list.component.ts b/client/src/app/+admin/follows/following-list/following-list.component.ts
index b97923f04..3d78c254f 100644
--- a/client/src/app/+admin/follows/following-list/following-list.component.ts
+++ b/client/src/app/+admin/follows/following-list/following-list.component.ts
@@ -50,7 +50,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
50 } 50 }
51 51
52 protected loadData () { 52 protected loadData () {
53 this.followService.getFollowing(this.pagination, this.sort, this.search) 53 this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search })
54 .subscribe( 54 .subscribe(
55 resultList => { 55 resultList => {
56 this.following = resultList.data 56 this.following = resultList.data
diff --git a/client/src/app/shared/instance/follow.service.ts b/client/src/app/shared/instance/follow.service.ts
index 63f9e2687..1477a26ae 100644
--- a/client/src/app/shared/instance/follow.service.ts
+++ b/client/src/app/shared/instance/follow.service.ts
@@ -2,7 +2,7 @@ import { catchError, map } from 'rxjs/operators'
2import { HttpClient, HttpParams } from '@angular/common/http' 2import { HttpClient, HttpParams } from '@angular/common/http'
3import { Injectable } from '@angular/core' 3import { Injectable } from '@angular/core'
4import { Observable } from 'rxjs' 4import { Observable } from 'rxjs'
5import { ActorFollow, ResultList } from '@shared/index' 5import { ActorFollow, FollowState, ResultList } from '@shared/index'
6import { environment } from '../../../environments/environment' 6import { environment } from '../../../environments/environment'
7import { RestExtractor, RestPagination, RestService } from '../rest' 7import { RestExtractor, RestPagination, RestService } from '../rest'
8import { SortMeta } from 'primeng/api' 8import { SortMeta } from 'primeng/api'
@@ -18,11 +18,19 @@ export class FollowService {
18 ) { 18 ) {
19 } 19 }
20 20
21 getFollowing (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<ActorFollow>> { 21 getFollowing (options: {
22 pagination: RestPagination,
23 sort: SortMeta,
24 search?: string,
25 state?: FollowState
26 }): Observable<ResultList<ActorFollow>> {
27 const { pagination, sort, search, state } = options
28
22 let params = new HttpParams() 29 let params = new HttpParams()
23 params = this.restService.addRestGetParams(params, pagination, sort) 30 params = this.restService.addRestGetParams(params, pagination, sort)
24 31
25 if (search) params = params.append('search', search) 32 if (search) params = params.append('search', search)
33 if (state) params = params.append('state', state)
26 34
27 return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/following', { params }) 35 return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/following', { params })
28 .pipe( 36 .pipe(
@@ -31,11 +39,19 @@ export class FollowService {
31 ) 39 )
32 } 40 }
33 41
34 getFollowers (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<ActorFollow>> { 42 getFollowers (options: {
43 pagination: RestPagination,
44 sort: SortMeta,
45 search?: string,
46 state?: FollowState
47 }): Observable<ResultList<ActorFollow>> {
48 const { pagination, sort, search, state } = options
49
35 let params = new HttpParams() 50 let params = new HttpParams()
36 params = this.restService.addRestGetParams(params, pagination, sort) 51 params = this.restService.addRestGetParams(params, pagination, sort)
37 52
38 if (search) params = params.append('search', search) 53 if (search) params = params.append('search', search)
54 if (state) params = params.append('state', state)
39 55
40 return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/followers', { params }) 56 return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/followers', { params })
41 .pipe( 57 .pipe(
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'
24import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 25import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
25import { JobQueue } from '../../../lib/job-queue' 26import { JobQueue } from '../../../lib/job-queue'
@@ -29,6 +30,7 @@ import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow'
29 30
30const serverFollowsRouter = express.Router() 31const serverFollowsRouter = express.Router()
31serverFollowsRouter.get('/following', 32serverFollowsRouter.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
54serverFollowsRouter.get('/followers', 56serverFollowsRouter.get('/followers',
57 listFollowsValidator,
55 paginationValidator, 58 paginationValidator,
56 followersSortValidator, 59 followersSortValidator,
57 setDefaultSort, 60 setDefaultSort,
@@ -92,26 +95,28 @@ export {
92 95
93async function listFollowing (req: express.Request, res: express.Response) { 96async 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
106async function listFollowers (req: express.Request, res: express.Response) { 110async 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 @@
1import { exists } from './misc'
2import { FollowState } from '@shared/models'
3
4function isFollowStateValid (value: FollowState) {
5 if (!exists(value)) return false
6
7 return value === 'pending' || value === 'accepted'
8}
9
10// ---------------------------------------------------------------------------
11
12export {
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 @@
1import * as express from 'express' 1import * as express from 'express'
2import { body, param } from 'express-validator' 2import { body, param, query } from 'express-validator'
3import { isTestInstance } from '../../helpers/core-utils' 3import { isTestInstance } from '../../helpers/core-utils'
4import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' 4import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers'
5import { logger } from '../../helpers/logger' 5import { logger } from '../../helpers/logger'
@@ -11,6 +11,19 @@ import { ActorModel } from '../../models/activitypub/actor'
11import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' 11import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
12import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' 12import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
13import { MActorFollowActorsDefault } from '@server/typings/models' 13import { MActorFollowActorsDefault } from '@server/typings/models'
14import { isFollowStateValid } from '@server/helpers/custom-validators/follows'
15
16const 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
15const followValidator = [ 28const 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 {
diff --git a/shared/extra-utils/server/follows.ts b/shared/extra-utils/server/follows.ts
index 0379bb109..365263a22 100644
--- a/shared/extra-utils/server/follows.ts
+++ b/shared/extra-utils/server/follows.ts
@@ -2,15 +2,17 @@ import * as request from 'supertest'
2import { ServerInfo } from './servers' 2import { ServerInfo } from './servers'
3import { waitJobs } from './jobs' 3import { waitJobs } from './jobs'
4import { makePostBodyRequest } from '../requests/requests' 4import { makePostBodyRequest } from '../requests/requests'
5import { FollowState } from '@shared/models'
5 6
6function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) { 7function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string, state?: FollowState) {
7 const path = '/api/v1/server/followers' 8 const path = '/api/v1/server/followers'
8 9
9 const query = { 10 const query = {
10 start, 11 start,
11 count, 12 count,
12 sort, 13 sort,
13 search 14 search,
15 state
14 } 16 }
15 17
16 return request(url) 18 return request(url)
@@ -43,14 +45,15 @@ function rejectFollower (url: string, token: string, follower: string, statusCod
43 }) 45 })
44} 46}
45 47
46function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) { 48function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string, state?: FollowState) {
47 const path = '/api/v1/server/following' 49 const path = '/api/v1/server/following'
48 50
49 const query = { 51 const query = {
50 start, 52 start,
51 count, 53 count,
52 sort, 54 sort,
53 search 55 search,
56 state
54 } 57 }
55 58
56 return request(url) 59 return request(url)