Better typescript typing for a better world
[oweals/peertube.git] / server / lib / request / request-video-qadu-scheduler.ts
1 import * as Sequelize from 'sequelize'
2
3 import { database as db } from '../../initializers/database'
4 import { AbstractRequestScheduler, RequestsObjects } from './abstract-request-scheduler'
5 import { logger } from '../../helpers'
6 import {
7   REQUESTS_VIDEO_QADU_LIMIT_PODS,
8   REQUESTS_VIDEO_QADU_LIMIT_PER_POD,
9   REQUEST_VIDEO_QADU_ENDPOINT,
10   REQUEST_VIDEO_QADU_TYPES
11 } from '../../initializers'
12 import { RequestsVideoQaduGrouped, PodInstance } from '../../models'
13 import { RemoteQaduVideoRequest, RequestVideoQaduType } from '../../../shared'
14
15 // We create a custom interface because we need "videos" attribute for our computations
16 interface RequestsObjectsCustom<U> extends RequestsObjects<U> {
17   [ id: string ]: {
18     toPod: PodInstance
19     endpoint: string
20     ids: number[] // ids
21     datas: U[]
22
23     videos: {
24       [ id: string ]: {
25         remoteId: string
26         likes?: number
27         dislikes?: number
28         views?: number
29       }
30     }
31   }
32 }
33
34 export type RequestVideoQaduSchedulerOptions = {
35   type: RequestVideoQaduType
36   videoId: string
37   transaction?: Sequelize.Transaction
38 }
39
40 class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQaduGrouped> {
41   constructor () {
42     super()
43
44     // We limit the size of the requests
45     this.limitPods = REQUESTS_VIDEO_QADU_LIMIT_PODS
46     this.limitPerPod = REQUESTS_VIDEO_QADU_LIMIT_PER_POD
47
48     this.description = 'video QADU requests'
49   }
50
51   getRequestModel () {
52     return db.RequestVideoQadu
53   }
54
55   getRequestToPodModel () {
56     return db.RequestVideoQadu
57   }
58
59   buildRequestsObjects (requests: RequestsVideoQaduGrouped) {
60     const requestsToMakeGrouped: RequestsObjectsCustom<RemoteQaduVideoRequest> = {}
61
62     Object.keys(requests).forEach(toPodId => {
63       requests[toPodId].forEach(data => {
64         const request = data.request
65         const video = data.video
66         const pod = data.pod
67         const hashKey = toPodId
68
69         if (!requestsToMakeGrouped[hashKey]) {
70           requestsToMakeGrouped[hashKey] = {
71             toPod: pod,
72             endpoint: REQUEST_VIDEO_QADU_ENDPOINT,
73             ids: [], // request ids, to delete them from the DB in the future
74             datas: [], // requests data
75             videos: {}
76           }
77         }
78
79         // Maybe another attribute was filled for this video
80         let videoData = requestsToMakeGrouped[hashKey].videos[video.id]
81         if (!videoData) videoData = { remoteId: null }
82
83         switch (request.type) {
84           case REQUEST_VIDEO_QADU_TYPES.LIKES:
85             videoData.likes = video.likes
86             break
87
88           case REQUEST_VIDEO_QADU_TYPES.DISLIKES:
89             videoData.dislikes = video.dislikes
90             break
91
92           case REQUEST_VIDEO_QADU_TYPES.VIEWS:
93             videoData.views = video.views
94             break
95
96           default:
97             logger.error('Unknown request video QADU type %s.', request.type)
98             return
99         }
100
101         // Do not forget the remoteId so the remote pod can identify the video
102         videoData.remoteId = video.id
103         requestsToMakeGrouped[hashKey].ids.push(request.id)
104
105         // Maybe there are multiple quick and dirty update for the same video
106         // We use this hashmap to dedupe them
107         requestsToMakeGrouped[hashKey].videos[video.id] = videoData
108       })
109     })
110
111     // Now we deduped similar quick and dirty updates, we can build our requests datas
112     Object.keys(requestsToMakeGrouped).forEach(hashKey => {
113       Object.keys(requestsToMakeGrouped[hashKey].videos).forEach(videoId => {
114         const videoData = requestsToMakeGrouped[hashKey].videos[videoId]
115
116         requestsToMakeGrouped[hashKey].datas.push({
117           data: videoData
118         })
119       })
120
121       // We don't need it anymore, it was just to build our datas array
122       delete requestsToMakeGrouped[hashKey].videos
123     })
124
125     return requestsToMakeGrouped
126   }
127
128   createRequest ({ type, videoId, transaction }: RequestVideoQaduSchedulerOptions) {
129     const dbRequestOptions: Sequelize.BulkCreateOptions = {}
130     if (transaction) dbRequestOptions.transaction = transaction
131
132     // Send the update to all our friends
133     return db.Pod.listAllIds(transaction).then(podIds => {
134       const queries = []
135       podIds.forEach(podId => {
136         queries.push({ type, videoId, podId })
137       })
138
139       return db.RequestVideoQadu.bulkCreate(queries, dbRequestOptions)
140     })
141   }
142 }
143
144 // ---------------------------------------------------------------------------
145
146 export {
147   RequestVideoQaduScheduler
148 }