diff options
author | Chocobozzz <me@florianbigard.com> | 2018-01-12 11:47:45 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-01-12 11:47:45 +0100 |
commit | 32b2b43c06621e384c0bd1610ef0bb9f23399be7 (patch) | |
tree | c195943354838bcbe3b1993d77aaf0d3200fd19d | |
parent | 6502c3d43e512e968ad49f5a3bc9abc302471e18 (diff) | |
download | PeerTube-32b2b43c06621e384c0bd1610ef0bb9f23399be7.tar.gz PeerTube-32b2b43c06621e384c0bd1610ef0bb9f23399be7.tar.zst PeerTube-32b2b43c06621e384c0bd1610ef0bb9f23399be7.zip |
Update follower/following counts
-rw-r--r-- | server/initializers/constants.ts | 2 | ||||
-rw-r--r-- | server/initializers/migrations/0175-actor-follow-counts.ts | 24 | ||||
-rw-r--r-- | server/lib/activitypub/actor.ts | 1 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-follow.ts | 3 | ||||
-rw-r--r-- | server/models/activitypub/actor-follow.ts | 20 | ||||
-rw-r--r-- | server/models/activitypub/actor.ts | 10 | ||||
-rw-r--r-- | server/tests/api/server/follows.ts | 37 | ||||
-rw-r--r-- | server/tests/utils/users/accounts.ts | 12 |
8 files changed, 108 insertions, 1 deletions
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index eda3403f2..2c64efe1f 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -9,7 +9,7 @@ import { isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core | |||
9 | 9 | ||
10 | // --------------------------------------------------------------------------- | 10 | // --------------------------------------------------------------------------- |
11 | 11 | ||
12 | const LAST_MIGRATION_VERSION = 170 | 12 | const LAST_MIGRATION_VERSION = 175 |
13 | 13 | ||
14 | // --------------------------------------------------------------------------- | 14 | // --------------------------------------------------------------------------- |
15 | 15 | ||
diff --git a/server/initializers/migrations/0175-actor-follow-counts.ts b/server/initializers/migrations/0175-actor-follow-counts.ts new file mode 100644 index 000000000..06ef77b49 --- /dev/null +++ b/server/initializers/migrations/0175-actor-follow-counts.ts | |||
@@ -0,0 +1,24 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { ACTOR_FOLLOW_SCORE } from '../index' | ||
3 | |||
4 | async function up (utils: { | ||
5 | transaction: Sequelize.Transaction, | ||
6 | queryInterface: Sequelize.QueryInterface, | ||
7 | sequelize: Sequelize.Sequelize | ||
8 | }): Promise<void> { | ||
9 | const query = 'UPDATE "actor" SET ' + | ||
10 | '"followersCount" = (SELECT COUNT(*) FROM "actorFollow" WHERE "actor"."id" = "actorFollow"."targetActorId"), ' + | ||
11 | '"followingCount" = (SELECT COUNT(*) FROM "actorFollow" WHERE "actor"."id" = "actorFollow"."actorId") ' + | ||
12 | 'WHERE "actor"."serverId" IS NULL' | ||
13 | |||
14 | await utils.sequelize.query(query) | ||
15 | } | ||
16 | |||
17 | function down (options) { | ||
18 | throw new Error('Not implemented.') | ||
19 | } | ||
20 | |||
21 | export { | ||
22 | up, | ||
23 | down | ||
24 | } | ||
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 1e1eab54a..b3fb75421 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -314,6 +314,7 @@ async function refreshActorIfNeeded (actor: ActorModel) { | |||
314 | if (result === undefined) throw new Error('Cannot fetch remote actor in refresh actor.') | 314 | if (result === undefined) throw new Error('Cannot fetch remote actor in refresh actor.') |
315 | 315 | ||
316 | return sequelizeTypescript.transaction(async t => { | 316 | return sequelizeTypescript.transaction(async t => { |
317 | logger.info('coucou', result.actor.toJSON()) | ||
317 | updateInstanceWithAnother(actor, result.actor) | 318 | updateInstanceWithAnother(actor, result.actor) |
318 | 319 | ||
319 | if (result.avatarName !== undefined) { | 320 | if (result.avatarName !== undefined) { |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 5085c5da9..69f5c51b5 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -51,6 +51,9 @@ async function follow (actor: ActorModel, targetActorURL: string) { | |||
51 | transaction: t | 51 | transaction: t |
52 | }) | 52 | }) |
53 | 53 | ||
54 | actorFollow.ActorFollower = actor | ||
55 | actorFollow.ActorFollowing = targetActor | ||
56 | |||
54 | if (actorFollow.state !== 'accepted') { | 57 | if (actorFollow.state !== 'accepted') { |
55 | actorFollow.state = 'accepted' | 58 | actorFollow.state = 'accepted' |
56 | await actorFollow.save({ transaction: t }) | 59 | await actorFollow.save({ transaction: t }) |
diff --git a/server/models/activitypub/actor-follow.ts b/server/models/activitypub/actor-follow.ts index de5bb6f74..435d22db5 100644 --- a/server/models/activitypub/actor-follow.ts +++ b/server/models/activitypub/actor-follow.ts | |||
@@ -2,6 +2,7 @@ import * as Bluebird from 'bluebird' | |||
2 | import { values } from 'lodash' | 2 | import { values } from 'lodash' |
3 | import * as Sequelize from 'sequelize' | 3 | import * as Sequelize from 'sequelize' |
4 | import { | 4 | import { |
5 | AfterCreate, AfterDestroy, AfterUpdate, | ||
5 | AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, IsInt, Max, Model, Table, | 6 | AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, IsInt, Max, Model, Table, |
6 | UpdatedAt | 7 | UpdatedAt |
7 | } from 'sequelize-typescript' | 8 | } from 'sequelize-typescript' |
@@ -79,6 +80,25 @@ export class ActorFollowModel extends Model<ActorFollowModel> { | |||
79 | }) | 80 | }) |
80 | ActorFollowing: ActorModel | 81 | ActorFollowing: ActorModel |
81 | 82 | ||
83 | @AfterCreate | ||
84 | @AfterUpdate | ||
85 | static incrementFollowerAndFollowingCount (instance: ActorFollowModel) { | ||
86 | if (instance.state !== 'accepted') return | ||
87 | |||
88 | return Promise.all([ | ||
89 | ActorModel.incrementFollows(instance.actorId, 'followingCount', 1), | ||
90 | ActorModel.incrementFollows(instance.targetActorId, 'followersCount', 1) | ||
91 | ]) | ||
92 | } | ||
93 | |||
94 | @AfterDestroy | ||
95 | static decrementFollowerAndFollowingCount (instance: ActorFollowModel) { | ||
96 | return Promise.all([ | ||
97 | ActorModel.incrementFollows(instance.actorId, 'followingCount',-1), | ||
98 | ActorModel.incrementFollows(instance.targetActorId, 'followersCount', -1) | ||
99 | ]) | ||
100 | } | ||
101 | |||
82 | // Remove actor follows with a score of 0 (too many requests where they were unreachable) | 102 | // Remove actor follows with a score of 0 (too many requests where they were unreachable) |
83 | static async removeBadActorFollows () { | 103 | static async removeBadActorFollows () { |
84 | const actorFollows = await ActorFollowModel.listBadActorFollows() | 104 | const actorFollows = await ActorFollowModel.listBadActorFollows() |
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 17f69f7a7..b7be9c32c 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -264,6 +264,16 @@ export class ActorModel extends Model<ActorModel> { | |||
264 | return ActorModel.scope(ScopeNames.FULL).findOne(query) | 264 | return ActorModel.scope(ScopeNames.FULL).findOne(query) |
265 | } | 265 | } |
266 | 266 | ||
267 | static incrementFollows (id: number, column: 'followersCount' | 'followingCount', by: number) { | ||
268 | // FIXME: typings | ||
269 | return (ActorModel as any).increment(column, { | ||
270 | by, | ||
271 | where: { | ||
272 | id | ||
273 | } | ||
274 | }) | ||
275 | } | ||
276 | |||
267 | toFormattedJSON () { | 277 | toFormattedJSON () { |
268 | let avatar: Avatar = null | 278 | let avatar: Avatar = null |
269 | if (this.Avatar) { | 279 | if (this.Avatar) { |
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts index e6dfd5f62..27cf94985 100644 --- a/server/tests/api/server/follows.ts +++ b/server/tests/api/server/follows.ts | |||
@@ -12,6 +12,7 @@ import { | |||
12 | } from '../../utils/index' | 12 | } from '../../utils/index' |
13 | import { dateIsValid } from '../../utils/miscs/miscs' | 13 | import { dateIsValid } from '../../utils/miscs/miscs' |
14 | import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows' | 14 | import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows' |
15 | import { expectAccountFollows } from '../../utils/users/accounts' | ||
15 | import { userLogin } from '../../utils/users/login' | 16 | import { userLogin } from '../../utils/users/login' |
16 | import { createUser } from '../../utils/users/users' | 17 | import { createUser } from '../../utils/users/users' |
17 | import { | 18 | import { |
@@ -116,6 +117,19 @@ describe('Test follows', function () { | |||
116 | expect(follows.length).to.equal(0) | 117 | expect(follows.length).to.equal(0) |
117 | }) | 118 | }) |
118 | 119 | ||
120 | it('Should have the correct following counts', async function () { | ||
121 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 2) | ||
122 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) | ||
123 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9003', 1, 0) | ||
124 | |||
125 | // Server 2 and 3 does not know server 1 follow another server (there was not a refresh) | ||
126 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) | ||
127 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) | ||
128 | |||
129 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 1) | ||
130 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 1, 0) | ||
131 | }) | ||
132 | |||
119 | it('Should unfollow server 3 on server 1', async function () { | 133 | it('Should unfollow server 3 on server 1', async function () { |
120 | this.timeout(5000) | 134 | this.timeout(5000) |
121 | 135 | ||
@@ -144,6 +158,17 @@ describe('Test follows', function () { | |||
144 | expect(follows.length).to.equal(0) | 158 | expect(follows.length).to.equal(0) |
145 | }) | 159 | }) |
146 | 160 | ||
161 | it('Should have the correct following counts 2', async function () { | ||
162 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 1) | ||
163 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) | ||
164 | |||
165 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) | ||
166 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) | ||
167 | |||
168 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 0) | ||
169 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 0, 0) | ||
170 | }) | ||
171 | |||
147 | it('Should upload a video on server 2 ans 3 and propagate only the video of server 2', async function () { | 172 | it('Should upload a video on server 2 ans 3 and propagate only the video of server 2', async function () { |
148 | this.timeout(10000) | 173 | this.timeout(10000) |
149 | 174 | ||
@@ -223,6 +248,18 @@ describe('Test follows', function () { | |||
223 | await wait(7000) | 248 | await wait(7000) |
224 | }) | 249 | }) |
225 | 250 | ||
251 | it('Should have the correct following counts 2', async function () { | ||
252 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9001', 0, 2) | ||
253 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9002', 1, 0) | ||
254 | await expectAccountFollows(servers[0].url, 'peertube@localhost:9003', 1, 0) | ||
255 | |||
256 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9001', 0, 1) | ||
257 | await expectAccountFollows(servers[1].url, 'peertube@localhost:9002', 1, 0) | ||
258 | |||
259 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9001', 0, 1) | ||
260 | await expectAccountFollows(servers[2].url, 'peertube@localhost:9003', 1, 0) | ||
261 | }) | ||
262 | |||
226 | it('Should propagate videos', async function () { | 263 | it('Should propagate videos', async function () { |
227 | const res = await getVideosList(servers[ 0 ].url) | 264 | const res = await getVideosList(servers[ 0 ].url) |
228 | expect(res.body.total).to.equal(7) | 265 | expect(res.body.total).to.equal(7) |
diff --git a/server/tests/utils/users/accounts.ts b/server/tests/utils/users/accounts.ts index 71712100e..0ec7992b3 100644 --- a/server/tests/utils/users/accounts.ts +++ b/server/tests/utils/users/accounts.ts | |||
@@ -1,3 +1,5 @@ | |||
1 | import { expect } from 'chai' | ||
2 | import { Account } from '../../../../shared/models/actors' | ||
1 | import { makeGetRequest } from '../requests/requests' | 3 | import { makeGetRequest } from '../requests/requests' |
2 | 4 | ||
3 | function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) { | 5 | function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) { |
@@ -21,9 +23,19 @@ function getAccount (url: string, accountId: number | string, statusCodeExpected | |||
21 | }) | 23 | }) |
22 | } | 24 | } |
23 | 25 | ||
26 | async function expectAccountFollows (url: string, nameWithDomain: string, followersCount: number, followingCount: number) { | ||
27 | const res = await getAccountsList(url) | ||
28 | const account = res.body.data.find((a: Account) => a.name + '@' + a.host === nameWithDomain) | ||
29 | |||
30 | const message = `${nameWithDomain} on ${url}` | ||
31 | expect(account.followersCount).to.equal(followersCount, message) | ||
32 | expect(account.followingCount).to.equal(followingCount, message) | ||
33 | } | ||
34 | |||
24 | // --------------------------------------------------------------------------- | 35 | // --------------------------------------------------------------------------- |
25 | 36 | ||
26 | export { | 37 | export { |
27 | getAccount, | 38 | getAccount, |
39 | expectAccountFollows, | ||
28 | getAccountsList | 40 | getAccountsList |
29 | } | 41 | } |