Only display accepted followers/followings in about page

pull/2301/head
Chocobozzz 2019-11-28 11:37:32 +01:00
parent d275e75453
commit b8f4167fb6
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
11 changed files with 171 additions and 35 deletions

View File

@ -74,7 +74,7 @@ export class AboutFollowsComponent implements OnInit {
private loadMoreFollowers () { private loadMoreFollowers () {
const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination) const pagination = this.restService.componentPaginationToRestPagination(this.followersPagination)
this.followService.getFollowers(pagination, this.sort) this.followService.getFollowers({ pagination: pagination, sort: this.sort, state: 'accepted' })
.subscribe( .subscribe(
resultList => { resultList => {
const newFollowers = resultList.data.map(r => r.follower.host) const newFollowers = resultList.data.map(r => r.follower.host)
@ -92,7 +92,7 @@ export class AboutFollowsComponent implements OnInit {
private loadMoreFollowings () { private loadMoreFollowings () {
const pagination = this.restService.componentPaginationToRestPagination(this.followingsPagination) const pagination = this.restService.componentPaginationToRestPagination(this.followingsPagination)
this.followService.getFollowing(pagination, this.sort) this.followService.getFollowing({ pagination, sort: this.sort, state: 'accepted' })
.subscribe( .subscribe(
resultList => { resultList => {
const newFollowings = resultList.data.map(r => r.following.host) const newFollowings = resultList.data.map(r => r.following.host)

View File

@ -88,7 +88,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
} }
protected loadData () { protected loadData () {
this.followService.getFollowers(this.pagination, this.sort, this.search) this.followService.getFollowers({ pagination: this.pagination, sort: this.sort, search: this.search })
.subscribe( .subscribe(
resultList => { resultList => {
this.followers = resultList.data this.followers = resultList.data

View File

@ -50,7 +50,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
} }
protected loadData () { protected loadData () {
this.followService.getFollowing(this.pagination, this.sort, this.search) this.followService.getFollowing({ pagination: this.pagination, sort: this.sort, search: this.search })
.subscribe( .subscribe(
resultList => { resultList => {
this.following = resultList.data this.following = resultList.data

View File

@ -2,7 +2,7 @@ import { catchError, map } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http' import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { ActorFollow, ResultList } from '@shared/index' import { ActorFollow, FollowState, ResultList } from '@shared/index'
import { environment } from '../../../environments/environment' import { environment } from '../../../environments/environment'
import { RestExtractor, RestPagination, RestService } from '../rest' import { RestExtractor, RestPagination, RestService } from '../rest'
import { SortMeta } from 'primeng/api' import { SortMeta } from 'primeng/api'
@ -18,11 +18,19 @@ export class FollowService {
) { ) {
} }
getFollowing (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<ActorFollow>> { getFollowing (options: {
pagination: RestPagination,
sort: SortMeta,
search?: string,
state?: FollowState
}): Observable<ResultList<ActorFollow>> {
const { pagination, sort, search, state } = options
let params = new HttpParams() let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
if (search) params = params.append('search', search) if (search) params = params.append('search', search)
if (state) params = params.append('state', state)
return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/following', { params }) return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/following', { params })
.pipe( .pipe(
@ -31,11 +39,19 @@ export class FollowService {
) )
} }
getFollowers (pagination: RestPagination, sort: SortMeta, search?: string): Observable<ResultList<ActorFollow>> { getFollowers (options: {
pagination: RestPagination,
sort: SortMeta,
search?: string,
state?: FollowState
}): Observable<ResultList<ActorFollow>> {
const { pagination, sort, search, state } = options
let params = new HttpParams() let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
if (search) params = params.append('search', search) if (search) params = params.append('search', search)
if (state) params = params.append('state', state)
return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/followers', { params }) return this.authHttp.get<ResultList<ActorFollow>>(FollowService.BASE_APPLICATION_URL + '/followers', { params })
.pipe( .pipe(

View File

@ -19,7 +19,8 @@ import {
followingSortValidator, followingSortValidator,
followValidator, followValidator,
getFollowerValidator, getFollowerValidator,
removeFollowingValidator removeFollowingValidator,
listFollowsValidator
} from '../../../middlewares/validators' } from '../../../middlewares/validators'
import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { JobQueue } from '../../../lib/job-queue' import { JobQueue } from '../../../lib/job-queue'
@ -29,6 +30,7 @@ import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow'
const serverFollowsRouter = express.Router() const serverFollowsRouter = express.Router()
serverFollowsRouter.get('/following', serverFollowsRouter.get('/following',
listFollowsValidator,
paginationValidator, paginationValidator,
followingSortValidator, followingSortValidator,
setDefaultSort, setDefaultSort,
@ -52,6 +54,7 @@ serverFollowsRouter.delete('/following/:host',
) )
serverFollowsRouter.get('/followers', serverFollowsRouter.get('/followers',
listFollowsValidator,
paginationValidator, paginationValidator,
followersSortValidator, followersSortValidator,
setDefaultSort, setDefaultSort,
@ -92,26 +95,28 @@ export {
async function listFollowing (req: express.Request, res: express.Response) { async function listFollowing (req: express.Request, res: express.Response) {
const serverActor = await getServerActor() const serverActor = await getServerActor()
const resultList = await ActorFollowModel.listFollowingForApi( const resultList = await ActorFollowModel.listFollowingForApi({
serverActor.id, id: serverActor.id,
req.query.start, start: req.query.start,
req.query.count, count: req.query.count,
req.query.sort, sort: req.query.sort,
req.query.search search: req.query.search,
) state: req.query.state
})
return res.json(getFormattedObjects(resultList.data, resultList.total)) return res.json(getFormattedObjects(resultList.data, resultList.total))
} }
async function listFollowers (req: express.Request, res: express.Response) { async function listFollowers (req: express.Request, res: express.Response) {
const serverActor = await getServerActor() const serverActor = await getServerActor()
const resultList = await ActorFollowModel.listFollowersForApi( const resultList = await ActorFollowModel.listFollowersForApi({
serverActor.id, actorId: serverActor.id,
req.query.start, start: req.query.start,
req.query.count, count: req.query.count,
req.query.sort, sort: req.query.sort,
req.query.search search: req.query.search,
) state: req.query.state
})
return res.json(getFormattedObjects(resultList.data, resultList.total)) return res.json(getFormattedObjects(resultList.data, resultList.total))
} }

View File

@ -0,0 +1,14 @@
import { exists } from './misc'
import { FollowState } from '@shared/models'
function isFollowStateValid (value: FollowState) {
if (!exists(value)) return false
return value === 'pending' || value === 'accepted'
}
// ---------------------------------------------------------------------------
export {
isFollowStateValid
}

View File

@ -1,5 +1,5 @@
import * as express from 'express' import * as express from 'express'
import { body, param } from 'express-validator' import { body, param, query } from 'express-validator'
import { isTestInstance } from '../../helpers/core-utils' import { isTestInstance } from '../../helpers/core-utils'
import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers' import { isEachUniqueHostValid, isHostValid } from '../../helpers/custom-validators/servers'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
@ -11,6 +11,19 @@ import { ActorModel } from '../../models/activitypub/actor'
import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger' import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor' import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
import { MActorFollowActorsDefault } from '@server/typings/models' import { MActorFollowActorsDefault } from '@server/typings/models'
import { isFollowStateValid } from '@server/helpers/custom-validators/follows'
const listFollowsValidator = [
query('state')
.optional()
.custom(isFollowStateValid).withMessage('Should have a valid follow state'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return
return next()
}
]
const followValidator = [ const followValidator = [
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'), body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
@ -110,5 +123,6 @@ export {
followValidator, followValidator,
removeFollowingValidator, removeFollowingValidator,
getFollowerValidator, getFollowerValidator,
acceptOrRejectFollowerValidator acceptOrRejectFollowerValidator,
listFollowsValidator
} }

View File

@ -292,12 +292,24 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
return ActorFollowModel.findAll(query) return ActorFollowModel.findAll(query)
} }
static listFollowingForApi (id: number, start: number, count: number, sort: string, search?: string) { static listFollowingForApi (options: {
id: number,
start: number,
count: number,
sort: string,
state?: FollowState,
search?: string
}) {
const { id, start, count, sort, search, state } = options
const followWhere = state ? { state } : {}
const query = { const query = {
distinct: true, distinct: true,
offset: start, offset: start,
limit: count, limit: count,
order: getSort(sort), order: getSort(sort),
where: followWhere,
include: [ include: [
{ {
model: ActorModel, model: ActorModel,
@ -335,12 +347,24 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
}) })
} }
static listFollowersForApi (actorId: number, start: number, count: number, sort: string, search?: string) { static listFollowersForApi (options: {
actorId: number,
start: number,
count: number,
sort: string,
state?: FollowState,
search?: string
}) {
const { actorId, start, count, sort, search, state } = options
const followWhere = state ? { state } : {}
const query = { const query = {
distinct: true, distinct: true,
offset: start, offset: start,
limit: count, limit: count,
order: getSort(sort), order: getSort(sort),
where: followWhere,
include: [ include: [
{ {
model: ActorModel, model: ActorModel,

View File

@ -6,7 +6,7 @@ import {
cleanupTests, cleanupTests,
createUser, createUser,
flushAndRunServer, flushAndRunServer,
makeDeleteRequest, makeDeleteRequest, makeGetRequest,
makePostBodyRequest, makePostBodyRequest,
ServerInfo, ServerInfo,
setAccessTokensToServers, setAccessTokensToServers,
@ -131,6 +131,27 @@ describe('Test server follows API validators', function () {
it('Should fail with an incorrect sort', async function () { it('Should fail with an incorrect sort', async function () {
await checkBadSortPagination(server.url, path) await checkBadSortPagination(server.url, path)
}) })
it('Should fail with an incorrect state', async function () {
await makeGetRequest({
url: server.url,
path,
query: {
state: 'blabla'
}
})
})
it('Should fail succeed with the correct params', async function () {
await makeGetRequest({
url: server.url,
path,
statusCodeExpected: 200,
query: {
state: 'accepted'
}
})
})
}) })
describe('When listing followers', function () { describe('When listing followers', function () {
@ -147,6 +168,27 @@ describe('Test server follows API validators', function () {
it('Should fail with an incorrect sort', async function () { it('Should fail with an incorrect sort', async function () {
await checkBadSortPagination(server.url, path) await checkBadSortPagination(server.url, path)
}) })
it('Should fail with an incorrect state', async function () {
await makeGetRequest({
url: server.url,
path,
query: {
state: 'blabla'
}
})
})
it('Should fail succeed with the correct params', async function () {
await makeGetRequest({
url: server.url,
path,
statusCodeExpected: 200,
query: {
state: 'accepted'
}
})
})
}) })
describe('When removing a follower', function () { describe('When removing a follower', function () {

View File

@ -97,14 +97,23 @@ describe('Test follows', function () {
expect(server3Follow.state).to.equal('accepted') expect(server3Follow.state).to.equal('accepted')
}) })
it('Should search followings on server 1', async function () { it('Should search/filter followings on server 1', async function () {
{ {
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', ':' + servers[1].port) const search = ':' + servers[1].port
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', search)
const follows = res.body.data const follows = res.body.data
expect(res.body.total).to.equal(1) expect(res.body.total).to.equal(1)
expect(follows.length).to.equal(1) expect(follows.length).to.equal(1)
expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[1].port) expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[1].port)
const res2 = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', search, 'accepted')
expect(res2.body.total).to.equal(1)
expect(res2.body.data).to.have.lengthOf(1)
const res3 = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', search, 'pending')
expect(res3.body.total).to.equal(0)
expect(res3.body.data).to.have.lengthOf(0)
} }
{ {
@ -139,14 +148,23 @@ describe('Test follows', function () {
} }
}) })
it('Should search followers on server 2', async function () { it('Should search/filter followers on server 2', async function () {
{ {
const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', servers[0].port + '') const search = servers[0].port + ''
const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', search)
const follows = res.body.data const follows = res.body.data
expect(res.body.total).to.equal(1) expect(res.body.total).to.equal(1)
expect(follows.length).to.equal(1) expect(follows.length).to.equal(1)
expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[2].port) expect(follows[ 0 ].following.host).to.equal('localhost:' + servers[2].port)
const res2 = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', search, 'accepted')
expect(res2.body.total).to.equal(1)
expect(res2.body.data).to.have.lengthOf(1)
const res3 = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', search, 'pending')
expect(res3.body.total).to.equal(0)
expect(res3.body.data).to.have.lengthOf(0)
} }
{ {

View File

@ -2,15 +2,17 @@ import * as request from 'supertest'
import { ServerInfo } from './servers' import { ServerInfo } from './servers'
import { waitJobs } from './jobs' import { waitJobs } from './jobs'
import { makePostBodyRequest } from '../requests/requests' import { makePostBodyRequest } from '../requests/requests'
import { FollowState } from '@shared/models'
function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) { function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string, state?: FollowState) {
const path = '/api/v1/server/followers' const path = '/api/v1/server/followers'
const query = { const query = {
start, start,
count, count,
sort, sort,
search search,
state
} }
return request(url) return request(url)
@ -43,14 +45,15 @@ function rejectFollower (url: string, token: string, follower: string, statusCod
}) })
} }
function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) { function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string, state?: FollowState) {
const path = '/api/v1/server/following' const path = '/api/v1/server/following'
const query = { const query = {
start, start,
count, count,
sort, sort,
search search,
state
} }
return request(url) return request(url)