1 import * as Bluebird from 'bluebird'
2 import { values } from 'lodash'
3 import * as Sequelize from 'sequelize'
4 import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
5 import { FollowState } from '../../../shared/models/actors'
6 import { FOLLOW_STATES } from '../../initializers/constants'
7 import { ServerModel } from '../server/server'
8 import { getSort } from '../utils'
9 import { ActorModel } from './actor'
12 tableName: 'actorFollow',
18 fields: [ 'targetActorId' ]
21 fields: [ 'actorId', 'targetActorId' ],
26 export class ActorFollowModel extends Model<ActorFollowModel> {
29 @Column(DataType.ENUM(values(FOLLOW_STATES)))
38 @ForeignKey(() => ActorModel)
42 @BelongsTo(() => ActorModel, {
50 ActorFollower: ActorModel
52 @ForeignKey(() => ActorModel)
56 @BelongsTo(() => ActorModel, {
58 name: 'targetActorId',
64 ActorFollowing: ActorModel
66 static loadByActorAndTarget (actorId: number, targetActorId: number, t?: Sequelize.Transaction) {
70 targetActorId: targetActorId
87 return ActorFollowModel.findOne(query)
90 static loadByActorAndTargetHost (actorId: number, targetHost: string, t?: Sequelize.Transaction) {
104 as: 'ActorFollowing',
119 return ActorFollowModel.findOne(query)
122 static listFollowingForApi (id: number, start: number, count: number, sort: string) {
127 order: [ getSort(sort) ],
139 as: 'ActorFollowing',
141 include: [ ServerModel ]
146 return ActorFollowModel.findAndCountAll(query)
147 .then(({ rows, count }) => {
155 static listFollowersForApi (id: number, start: number, count: number, sort: string) {
160 order: [ getSort(sort) ],
166 include: [ ServerModel ]
170 as: 'ActorFollowing',
179 return ActorFollowModel.findAndCountAll(query)
180 .then(({ rows, count }) => {
188 static listAcceptedFollowerUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
189 return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, start, count)
192 static listAcceptedFollowerSharedInboxUrls (actorIds: number[], t: Sequelize.Transaction) {
193 return ActorFollowModel.createListAcceptedFollowForApiQuery('followers', actorIds, t, undefined, undefined, 'sharedInboxUrl')
196 static listAcceptedFollowingUrlsForApi (actorIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
197 return ActorFollowModel.createListAcceptedFollowForApiQuery('following', actorIds, t, start, count)
200 private static async createListAcceptedFollowForApiQuery (type: 'followers' | 'following',
202 t: Sequelize.Transaction,
206 let firstJoin: string
207 let secondJoin: string
209 if (type === 'followers') {
210 firstJoin = 'targetActorId'
211 secondJoin = 'actorId'
213 firstJoin = 'actorId'
214 secondJoin = 'targetActorId'
217 const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
218 const tasks: Bluebird<any>[] = []
220 for (const selection of selections) {
221 let query = 'SELECT ' + selection + ' FROM "actor" ' +
222 'INNER JOIN "actorFollow" ON "actorFollow"."' + firstJoin + '" = "actor"."id" ' +
223 'INNER JOIN "actor" AS "Follows" ON "actorFollow"."' + secondJoin + '" = "Follows"."id" ' +
224 'WHERE "actor"."id" = ANY ($actorIds) AND "actorFollow"."state" = \'accepted\' '
226 if (count !== undefined) query += 'LIMIT ' + count
227 if (start !== undefined) query += ' OFFSET ' + start
231 type: Sequelize.QueryTypes.SELECT,
234 tasks.push(ActorFollowModel.sequelize.query(query, options))
237 const [ followers, [ { total } ] ] = await
239 const urls: string[] = followers.map(f => f.url)
243 total: parseInt(total, 10)
248 const follower = this.ActorFollower.toFormattedJSON()
249 const following = this.ActorFollowing.toFormattedJSON()
256 createdAt: this.createdAt,
257 updatedAt: this.updatedAt