1
0
Fork 0

Filter on follows actor types in about page

This commit is contained in:
Chocobozzz 2019-11-29 10:55:17 +01:00
parent f5b72c3937
commit 97ecddae10
No known key found for this signature in database
GPG key ID: 583A612D890159BE
12 changed files with 215 additions and 75 deletions

View file

@ -1,8 +1,8 @@
<div class="row" myInfiniteScroller [autoInit]="true" (nearOfBottom)="onNearOfBottom()" [dataObservable]="onDataSubject.asObservable()">
<div class="col-xl-6 col-md-12">
<div i18n class="subtitle">Followers</div>
<div i18n class="subtitle">Followers instances</div>
<div i18n class="no-results" *ngIf="followersPagination.totalItems === 0">This instance does not have followers.</div>
<div i18n class="no-results" *ngIf="followersPagination.totalItems === 0">This instance does not have instances followers.</div>
<a *ngFor="let follower of followers" [href]="buildLink(follower)" target="_blank" rel="noopener noreferrer">
{{ follower }}
@ -10,9 +10,9 @@
</div>
<div class="col-xl-6 col-md-12">
<div i18n class="subtitle">Followings</div>
<div i18n class="subtitle">Followings instances</div>
<div i18n class="no-results" *ngIf="followingsPagination.totalItems === 0">This instance does not have followings.</div>
<div i18n class="no-results" *ngIf="followingsPagination.totalItems === 0">This instance does not have instances followings.</div>
<a *ngFor="let following of followings" [href]="buildLink(following)" target="_blank" rel="noopener noreferrer">
{{ following }}

View file

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

View file

@ -101,6 +101,7 @@ async function listFollowing (req: express.Request, res: express.Response) {
count: req.query.count,
sort: req.query.sort,
search: req.query.search,
actorType: req.query.actorType,
state: req.query.state
})
@ -115,6 +116,7 @@ async function listFollowers (req: express.Request, res: express.Response) {
count: req.query.count,
sort: req.query.sort,
search: req.query.search,
actorType: req.query.actorType,
state: req.query.state
})

View file

@ -9,7 +9,7 @@ import { ActorFollowModel } from '../../models/activitypub/actor-follow'
import { areValidationErrors } from './utils'
import { ActorModel } from '../../models/activitypub/actor'
import { loadActorUrlOrGetFromWebfinger } from '../../helpers/webfinger'
import { isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
import { isActorTypeValid, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
import { MActorFollowActorsDefault } from '@server/typings/models'
import { isFollowStateValid } from '@server/helpers/custom-validators/follows'
@ -17,6 +17,9 @@ const listFollowsValidator = [
query('state')
.optional()
.custom(isFollowStateValid).withMessage('Should have a valid follow state'),
query('actorType')
.optional()
.custom(isActorTypeValid).withMessage('Should have a valid actor type'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
if (areValidationErrors(req, res)) return

View file

@ -27,7 +27,7 @@ import { createSafeIn, getSort } from '../utils'
import { ActorModel, unusedActorAttributesForAPI } from './actor'
import { VideoChannelModel } from '../video/video-channel'
import { AccountModel } from '../account/account'
import { IncludeOptions, Op, QueryTypes, Transaction } from 'sequelize'
import { IncludeOptions, Op, QueryTypes, Transaction, WhereOptions } from 'sequelize'
import {
MActorFollowActorsDefault,
MActorFollowActorsDefaultSubscription,
@ -35,6 +35,7 @@ import {
MActorFollowFormattable,
MActorFollowSubscriptions
} from '@server/typings/models'
import { ActivityPubActorType } from '@shared/models'
@Table({
tableName: 'actorFollow',
@ -298,11 +299,26 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
count: number,
sort: string,
state?: FollowState,
actorType?: ActivityPubActorType,
search?: string
}) {
const { id, start, count, sort, search, state } = options
const { id, start, count, sort, search, state, actorType } = options
const followWhere = state ? { state } : {}
const followingWhere: WhereOptions = {}
const followingServerWhere: WhereOptions = {}
if (search) {
Object.assign(followingServerWhere, {
host: {
[ Op.iLike ]: '%' + search + '%'
}
})
}
if (actorType) {
Object.assign(followingWhere, { type: actorType })
}
const query = {
distinct: true,
@ -323,15 +339,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
model: ActorModel,
as: 'ActorFollowing',
required: true,
where: followingWhere,
include: [
{
model: ServerModel,
required: true,
where: search ? {
host: {
[Op.iLike]: '%' + search + '%'
}
} : undefined
where: followingServerWhere
}
]
}
@ -353,11 +366,26 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
count: number,
sort: string,
state?: FollowState,
actorType?: ActivityPubActorType,
search?: string
}) {
const { actorId, start, count, sort, search, state } = options
const { actorId, start, count, sort, search, state, actorType } = options
const followWhere = state ? { state } : {}
const followerWhere: WhereOptions = {}
const followerServerWhere: WhereOptions = {}
if (search) {
Object.assign(followerServerWhere, {
host: {
[ Op.iLike ]: '%' + search + '%'
}
})
}
if (actorType) {
Object.assign(followerWhere, { type: actorType })
}
const query = {
distinct: true,
@ -370,15 +398,12 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
model: ActorModel,
required: true,
as: 'ActorFollower',
where: followerWhere,
include: [
{
model: ServerModel,
required: true,
where: search ? {
host: {
[ Op.iLike ]: '%' + search + '%'
}
} : undefined
where: followerServerWhere
}
]
},

View file

@ -142,13 +142,24 @@ describe('Test server follows API validators', function () {
})
})
it('Should fail with an incorrect actor type', async function () {
await makeGetRequest({
url: server.url,
path,
query: {
actorType: 'blabla'
}
})
})
it('Should fail succeed with the correct params', async function () {
await makeGetRequest({
url: server.url,
path,
statusCodeExpected: 200,
query: {
state: 'accepted'
state: 'accepted',
actorType: 'Application'
}
})
})
@ -169,12 +180,23 @@ describe('Test server follows API validators', function () {
await checkBadSortPagination(server.url, path)
})
it('Should fail with an incorrect actor type', async function () {
await makeGetRequest({
url: server.url,
path,
query: {
actorType: 'blabla'
}
})
})
it('Should fail with an incorrect state', async function () {
await makeGetRequest({
url: server.url,
path,
query: {
state: 'blabla'
state: 'blabla',
actorType: 'Application'
}
})
})

View file

@ -247,7 +247,7 @@ async function checkStatsWith1Webseed (strategy: VideoRedundancyStrategy) {
async function enableRedundancyOnServer1 () {
await updateRedundancy(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ].host, true)
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt')
const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: '-createdAt' })
const follows: ActorFollow[] = res.body.data
const server2 = follows.find(f => f.following.host === `localhost:${servers[ 1 ].port}`)
const server3 = follows.find(f => f.following.host === `localhost:${servers[ 2 ].port}`)
@ -262,7 +262,7 @@ async function enableRedundancyOnServer1 () {
async function disableRedundancyOnServer1 () {
await updateRedundancy(servers[ 0 ].url, servers[ 0 ].accessToken, servers[ 1 ].host, false)
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, '-createdAt')
const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: '-createdAt' })
const follows: ActorFollow[] = res.body.data
const server2 = follows.find(f => f.following.host === `localhost:${servers[ 1 ].port}`)
const server3 = follows.find(f => f.following.host === `localhost:${servers[ 2 ].port}`)

View file

@ -21,7 +21,7 @@ const expect = chai.expect
async function checkFollow (follower: ServerInfo, following: ServerInfo, exists: boolean) {
{
const res = await getFollowersListPaginationAndSort(following.url, 0, 5, '-createdAt')
const res = await getFollowersListPaginationAndSort({ url: following.url, start: 0, count: 5, sort: '-createdAt' })
const follows = res.body.data as ActorFollow[]
const follow = follows.find(f => {
@ -36,7 +36,7 @@ async function checkFollow (follower: ServerInfo, following: ServerInfo, exists:
}
{
const res = await getFollowingListPaginationAndSort(follower.url, 0, 5, '-createdAt')
const res = await getFollowingListPaginationAndSort({ url: follower.url, start: 0, count: 5, sort: '-createdAt' })
const follows = res.body.data as ActorFollow[]
const follow = follows.find(f => {

View file

@ -24,7 +24,7 @@ const expect = chai.expect
async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'accepted') {
{
const res = await getFollowingListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: 'createdAt' })
expect(res.body.total).to.equal(1)
const follow = res.body.data[0] as ActorFollow
@ -34,7 +34,7 @@ async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'acc
}
{
const res = await getFollowersListPaginationAndSort(servers[1].url, 0, 5, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 1 ].url, start: 0, count: 5, sort: 'createdAt' })
expect(res.body.total).to.equal(1)
const follow = res.body.data[0] as ActorFollow
@ -46,12 +46,12 @@ async function checkServer1And2HasFollowers (servers: ServerInfo[], state = 'acc
async function checkNoFollowers (servers: ServerInfo[]) {
{
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 5, 'createdAt')
const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: 'createdAt' })
expect(res.body.total).to.equal(0)
}
{
const res = await getFollowersListPaginationAndSort(servers[ 1 ].url, 0, 5, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 1 ].url, start: 0, count: 5, sort: 'createdAt' })
expect(res.body.total).to.equal(0)
}
}
@ -164,17 +164,17 @@ describe('Test follows moderation', function () {
await waitJobs(servers)
{
const res = await getFollowingListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: 'createdAt' })
expect(res.body.total).to.equal(2)
}
{
const res = await getFollowersListPaginationAndSort(servers[1].url, 0, 5, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 1 ].url, start: 0, count: 5, sort: 'createdAt' })
expect(res.body.total).to.equal(1)
}
{
const res = await getFollowersListPaginationAndSort(servers[2].url, 0, 5, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 2 ].url, start: 0, count: 5, sort: 'createdAt' })
expect(res.body.total).to.equal(1)
}
@ -184,7 +184,7 @@ describe('Test follows moderation', function () {
await checkServer1And2HasFollowers(servers)
{
const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 2 ].url, start: 0, count: 5, sort: 'createdAt' })
expect(res.body.total).to.equal(0)
}
})

View file

@ -49,7 +49,7 @@ describe('Test follows', function () {
it('Should not have followers', async function () {
for (const server of servers) {
const res = await getFollowersListPaginationAndSort(server.url, 0, 5, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
const follows = res.body.data
expect(res.body.total).to.equal(0)
@ -60,7 +60,7 @@ describe('Test follows', function () {
it('Should not have following', async function () {
for (const server of servers) {
const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
const res = await getFollowingListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
const follows = res.body.data
expect(res.body.total).to.equal(0)
@ -78,14 +78,14 @@ describe('Test follows', function () {
})
it('Should have 2 followings on server 1', async function () {
let res = await getFollowingListPaginationAndSort(servers[0].url, 0, 1, 'createdAt')
let res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 1, sort: 'createdAt' })
let follows = res.body.data
expect(res.body.total).to.equal(2)
expect(follows).to.be.an('array')
expect(follows.length).to.equal(1)
res = await getFollowingListPaginationAndSort(servers[0].url, 1, 1, 'createdAt')
res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 1, count: 1, sort: 'createdAt' })
follows = follows.concat(res.body.data)
const server2Follow = follows.find(f => f.following.host === 'localhost:' + servers[1].port)
@ -98,26 +98,58 @@ describe('Test follows', function () {
})
it('Should search/filter followings on server 1', async function () {
const sort = 'createdAt'
const start = 0
const count = 1
const url = servers[ 0 ].url
{
const search = ':' + servers[1].port
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', search)
{
const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search })
const follows = res.body.data
expect(res.body.total).to.equal(1)
expect(follows.length).to.equal(1)
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)
}
{
const res = await getFollowingListPaginationAndSort(servers[ 0 ].url, 0, 1, 'createdAt', 'bla')
const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'accepted' })
expect(res.body.total).to.equal(1)
expect(res.body.data).to.have.lengthOf(1)
}
{
const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'accepted', actorType: 'Person' })
expect(res.body.total).to.equal(0)
expect(res.body.data).to.have.lengthOf(0)
}
{
const res = await getFollowingListPaginationAndSort({
url,
start,
count,
sort,
search,
state: 'accepted',
actorType: 'Application'
})
expect(res.body.total).to.equal(1)
expect(res.body.data).to.have.lengthOf(1)
}
{
const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search, state: 'pending' })
expect(res.body.total).to.equal(0)
expect(res.body.data).to.have.lengthOf(0)
}
}
{
const res = await getFollowingListPaginationAndSort({ url, start, count, sort, search: 'bla' })
const follows = res.body.data
expect(res.body.total).to.equal(0)
@ -127,7 +159,7 @@ describe('Test follows', function () {
it('Should have 0 followings on server 2 and 3', async function () {
for (const server of [ servers[1], servers[2] ]) {
const res = await getFollowingListPaginationAndSort(server.url, 0, 5, 'createdAt')
const res = await getFollowingListPaginationAndSort({ url: server.url, start: 0, count: 5, sort: 'createdAt' })
const follows = res.body.data
expect(res.body.total).to.equal(0)
@ -138,7 +170,7 @@ describe('Test follows', function () {
it('Should have 1 followers on server 2 and 3', async function () {
for (const server of [ servers[1], servers[2] ]) {
let res = await getFollowersListPaginationAndSort(server.url, 0, 1, 'createdAt')
let res = await getFollowersListPaginationAndSort({ url: server.url, start: 0, count: 1, sort: 'createdAt' })
let follows = res.body.data
expect(res.body.total).to.equal(1)
@ -149,26 +181,58 @@ describe('Test follows', function () {
})
it('Should search/filter followers on server 2', async function () {
const url = servers[ 2 ].url
const start = 0
const count = 5
const sort = 'createdAt'
{
const search = servers[0].port + ''
const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', search)
{
const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search })
const follows = res.body.data
expect(res.body.total).to.equal(1)
expect(follows.length).to.equal(1)
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)
}
{
const res = await getFollowersListPaginationAndSort(servers[ 2 ].url, 0, 5, 'createdAt', 'bla')
const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'accepted' })
expect(res.body.total).to.equal(1)
expect(res.body.data).to.have.lengthOf(1)
}
{
const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'accepted', actorType: 'Person' })
expect(res.body.total).to.equal(0)
expect(res.body.data).to.have.lengthOf(0)
}
{
const res = await getFollowersListPaginationAndSort({
url,
start,
count,
sort,
search,
state: 'accepted',
actorType: 'Application'
})
expect(res.body.total).to.equal(1)
expect(res.body.data).to.have.lengthOf(1)
}
{
const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search, state: 'pending' })
expect(res.body.total).to.equal(0)
expect(res.body.data).to.have.lengthOf(0)
}
}
{
const res = await getFollowersListPaginationAndSort({ url, start, count, sort, search: 'bla' })
const follows = res.body.data
expect(res.body.total).to.equal(0)
@ -177,7 +241,7 @@ describe('Test follows', function () {
})
it('Should have 0 followers on server 1', async function () {
const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 5, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 5, sort: 'createdAt' })
const follows = res.body.data
expect(res.body.total).to.equal(0)
@ -207,7 +271,7 @@ describe('Test follows', function () {
})
it('Should not follow server 3 on server 1 anymore', async function () {
const res = await getFollowingListPaginationAndSort(servers[0].url, 0, 2, 'createdAt')
const res = await getFollowingListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 2, sort: 'createdAt' })
let follows = res.body.data
expect(res.body.total).to.equal(1)
@ -218,7 +282,7 @@ describe('Test follows', function () {
})
it('Should not have server 1 as follower on server 3 anymore', async function () {
const res = await getFollowersListPaginationAndSort(servers[2].url, 0, 1, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 2 ].url, start: 0, count: 1, sort: 'createdAt' })
let follows = res.body.data
expect(res.body.total).to.equal(0)

View file

@ -174,7 +174,7 @@ describe('Test handle downs', function () {
await wait(11000)
// Only server 3 is still a follower of server 1
const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 2, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 2, sort: 'createdAt' })
expect(res.body.data).to.be.an('array')
expect(res.body.data).to.have.lengthOf(1)
expect(res.body.data[0].follower.host).to.equal('localhost:' + servers[2].port)
@ -202,7 +202,7 @@ describe('Test handle downs', function () {
await waitJobs(servers)
const res = await getFollowersListPaginationAndSort(servers[0].url, 0, 2, 'createdAt')
const res = await getFollowersListPaginationAndSort({ url: servers[ 0 ].url, start: 0, count: 2, sort: 'createdAt' })
expect(res.body.data).to.be.an('array')
expect(res.body.data).to.have.lengthOf(2)
})

View file

@ -2,9 +2,18 @@ import * as request from 'supertest'
import { ServerInfo } from './servers'
import { waitJobs } from './jobs'
import { makePostBodyRequest } from '../requests/requests'
import { FollowState } from '@shared/models'
import { ActivityPubActorType, FollowState } from '@shared/models'
function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string, state?: FollowState) {
function getFollowersListPaginationAndSort (options: {
url: string,
start: number,
count: number,
sort: string,
search?: string,
actorType?: ActivityPubActorType,
state?: FollowState
}) {
const { url, start, count, sort, search, state, actorType } = options
const path = '/api/v1/server/followers'
const query = {
@ -12,7 +21,8 @@ function getFollowersListPaginationAndSort (url: string, start: number, count: n
count,
sort,
search,
state
state,
actorType
}
return request(url)
@ -45,7 +55,16 @@ function rejectFollower (url: string, token: string, follower: string, statusCod
})
}
function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string, state?: FollowState) {
function getFollowingListPaginationAndSort (options: {
url: string,
start: number,
count: number,
sort: string,
search?: string,
actorType?: ActivityPubActorType,
state?: FollowState
}) {
const { url, start, count, sort, search, state, actorType } = options
const path = '/api/v1/server/following'
const query = {
@ -53,7 +72,8 @@ function getFollowingListPaginationAndSort (url: string, start: number, count: n
count,
sort,
search,
state
state,
actorType
}
return request(url)