diff options
author | Chocobozzz <me@florianbigard.com> | 2022-01-18 11:23:41 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-01-18 11:23:41 +0100 |
commit | 7177b46ca1b35aa9d7ed39a06c1dcf41a4fc6180 (patch) | |
tree | 016cb0d966fe9fea8a6381eb246e966f5c4eae57 /server | |
parent | 3b83faccfffc13adaef0b63c211b1ce4944e8b3b (diff) | |
download | PeerTube-7177b46ca1b35aa9d7ed39a06c1dcf41a4fc6180.tar.gz PeerTube-7177b46ca1b35aa9d7ed39a06c1dcf41a4fc6180.tar.zst PeerTube-7177b46ca1b35aa9d7ed39a06c1dcf41a4fc6180.zip |
Add ability to delete history element
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/users/my-history.ts | 23 | ||||
-rw-r--r-- | server/middlewares/validators/user-history.ts | 24 | ||||
-rw-r--r-- | server/models/user/user-video-history.ts | 11 | ||||
-rw-r--r-- | server/tests/api/check-params/videos-history.ts | 37 | ||||
-rw-r--r-- | server/tests/api/videos/videos-history.ts | 36 |
5 files changed, 113 insertions, 18 deletions
diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts index 2fcb25acf..bc5b40f59 100644 --- a/server/controllers/api/users/my-history.ts +++ b/server/controllers/api/users/my-history.ts | |||
@@ -9,7 +9,8 @@ import { | |||
9 | paginationValidator, | 9 | paginationValidator, |
10 | setDefaultPagination, | 10 | setDefaultPagination, |
11 | userHistoryListValidator, | 11 | userHistoryListValidator, |
12 | userHistoryRemoveValidator | 12 | userHistoryRemoveAllValidator, |
13 | userHistoryRemoveElementValidator | ||
13 | } from '../../../middlewares' | 14 | } from '../../../middlewares' |
14 | import { UserVideoHistoryModel } from '../../../models/user/user-video-history' | 15 | import { UserVideoHistoryModel } from '../../../models/user/user-video-history' |
15 | 16 | ||
@@ -23,10 +24,16 @@ myVideosHistoryRouter.get('/me/history/videos', | |||
23 | asyncMiddleware(listMyVideosHistory) | 24 | asyncMiddleware(listMyVideosHistory) |
24 | ) | 25 | ) |
25 | 26 | ||
27 | myVideosHistoryRouter.delete('/me/history/videos/:videoId', | ||
28 | authenticate, | ||
29 | userHistoryRemoveElementValidator, | ||
30 | asyncMiddleware(removeUserHistoryElement) | ||
31 | ) | ||
32 | |||
26 | myVideosHistoryRouter.post('/me/history/videos/remove', | 33 | myVideosHistoryRouter.post('/me/history/videos/remove', |
27 | authenticate, | 34 | authenticate, |
28 | userHistoryRemoveValidator, | 35 | userHistoryRemoveAllValidator, |
29 | asyncRetryTransactionMiddleware(removeUserHistory) | 36 | asyncRetryTransactionMiddleware(removeAllUserHistory) |
30 | ) | 37 | ) |
31 | 38 | ||
32 | // --------------------------------------------------------------------------- | 39 | // --------------------------------------------------------------------------- |
@@ -45,7 +52,15 @@ async function listMyVideosHistory (req: express.Request, res: express.Response) | |||
45 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 52 | return res.json(getFormattedObjects(resultList.data, resultList.total)) |
46 | } | 53 | } |
47 | 54 | ||
48 | async function removeUserHistory (req: express.Request, res: express.Response) { | 55 | async function removeUserHistoryElement (req: express.Request, res: express.Response) { |
56 | const user = res.locals.oauth.token.User | ||
57 | |||
58 | await UserVideoHistoryModel.removeUserHistoryElement(user, parseInt(req.params.videoId + '')) | ||
59 | |||
60 | return res.sendStatus(HttpStatusCode.NO_CONTENT_204) | ||
61 | } | ||
62 | |||
63 | async function removeAllUserHistory (req: express.Request, res: express.Response) { | ||
49 | const user = res.locals.oauth.token.User | 64 | const user = res.locals.oauth.token.User |
50 | const beforeDate = req.body.beforeDate || null | 65 | const beforeDate = req.body.beforeDate || null |
51 | 66 | ||
diff --git a/server/middlewares/validators/user-history.ts b/server/middlewares/validators/user-history.ts index f9be26627..541910be5 100644 --- a/server/middlewares/validators/user-history.ts +++ b/server/middlewares/validators/user-history.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import express from 'express' | 1 | import express from 'express' |
2 | import { body, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { exists, isDateValid } from '../../helpers/custom-validators/misc' | 3 | import { exists, isDateValid, isIdValid } from '../../helpers/custom-validators/misc' |
4 | import { logger } from '../../helpers/logger' | 4 | import { logger } from '../../helpers/logger' |
5 | import { areValidationErrors } from './shared' | 5 | import { areValidationErrors } from './shared' |
6 | 6 | ||
@@ -18,13 +18,26 @@ const userHistoryListValidator = [ | |||
18 | } | 18 | } |
19 | ] | 19 | ] |
20 | 20 | ||
21 | const userHistoryRemoveValidator = [ | 21 | const userHistoryRemoveAllValidator = [ |
22 | body('beforeDate') | 22 | body('beforeDate') |
23 | .optional() | 23 | .optional() |
24 | .custom(isDateValid).withMessage('Should have a before date that conforms to ISO 8601'), | 24 | .custom(isDateValid).withMessage('Should have a before date that conforms to ISO 8601'), |
25 | 25 | ||
26 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 26 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
27 | logger.debug('Checking userHistoryRemoveValidator parameters', { parameters: req.body }) | 27 | logger.debug('Checking userHistoryRemoveAllValidator parameters', { parameters: req.body }) |
28 | |||
29 | if (areValidationErrors(req, res)) return | ||
30 | |||
31 | return next() | ||
32 | } | ||
33 | ] | ||
34 | |||
35 | const userHistoryRemoveElementValidator = [ | ||
36 | param('videoId') | ||
37 | .custom(isIdValid).withMessage('Should have a valid video id'), | ||
38 | |||
39 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
40 | logger.debug('Checking userHistoryRemoveElementValidator parameters', { parameters: req.params }) | ||
28 | 41 | ||
29 | if (areValidationErrors(req, res)) return | 42 | if (areValidationErrors(req, res)) return |
30 | 43 | ||
@@ -36,5 +49,6 @@ const userHistoryRemoveValidator = [ | |||
36 | 49 | ||
37 | export { | 50 | export { |
38 | userHistoryListValidator, | 51 | userHistoryListValidator, |
39 | userHistoryRemoveValidator | 52 | userHistoryRemoveElementValidator, |
53 | userHistoryRemoveAllValidator | ||
40 | } | 54 | } |
diff --git a/server/models/user/user-video-history.ts b/server/models/user/user-video-history.ts index 92f4fe7a1..f4d0889a1 100644 --- a/server/models/user/user-video-history.ts +++ b/server/models/user/user-video-history.ts | |||
@@ -69,6 +69,17 @@ export class UserVideoHistoryModel extends Model<Partial<AttributesOnly<UserVide | |||
69 | }) | 69 | }) |
70 | } | 70 | } |
71 | 71 | ||
72 | static removeUserHistoryElement (user: MUserId, videoId: number) { | ||
73 | const query: DestroyOptions = { | ||
74 | where: { | ||
75 | userId: user.id, | ||
76 | videoId | ||
77 | } | ||
78 | } | ||
79 | |||
80 | return UserVideoHistoryModel.destroy(query) | ||
81 | } | ||
82 | |||
72 | static removeUserHistoryBefore (user: MUserId, beforeDate: string, t: Transaction) { | 83 | static removeUserHistoryBefore (user: MUserId, beforeDate: string, t: Transaction) { |
73 | const query: DestroyOptions = { | 84 | const query: DestroyOptions = { |
74 | where: { | 85 | where: { |
diff --git a/server/tests/api/check-params/videos-history.ts b/server/tests/api/check-params/videos-history.ts index 31a0752c7..82f38b7b4 100644 --- a/server/tests/api/check-params/videos-history.ts +++ b/server/tests/api/check-params/videos-history.ts | |||
@@ -6,6 +6,7 @@ import { HttpStatusCode } from '@shared/models' | |||
6 | import { | 6 | import { |
7 | cleanupTests, | 7 | cleanupTests, |
8 | createSingleServer, | 8 | createSingleServer, |
9 | makeDeleteRequest, | ||
9 | makeGetRequest, | 10 | makeGetRequest, |
10 | makePostBodyRequest, | 11 | makePostBodyRequest, |
11 | makePutBodyRequest, | 12 | makePutBodyRequest, |
@@ -18,6 +19,7 @@ describe('Test videos history API validator', function () { | |||
18 | const myHistoryRemove = myHistoryPath + '/remove' | 19 | const myHistoryRemove = myHistoryPath + '/remove' |
19 | let watchingPath: string | 20 | let watchingPath: string |
20 | let server: PeerTubeServer | 21 | let server: PeerTubeServer |
22 | let videoId: number | ||
21 | 23 | ||
22 | // --------------------------------------------------------------- | 24 | // --------------------------------------------------------------- |
23 | 25 | ||
@@ -28,8 +30,9 @@ describe('Test videos history API validator', function () { | |||
28 | 30 | ||
29 | await setAccessTokensToServers([ server ]) | 31 | await setAccessTokensToServers([ server ]) |
30 | 32 | ||
31 | const { uuid } = await server.videos.upload() | 33 | const { id, uuid } = await server.videos.upload() |
32 | watchingPath = '/api/v1/videos/' + uuid + '/watching' | 34 | watchingPath = '/api/v1/videos/' + uuid + '/watching' |
35 | videoId = id | ||
33 | }) | 36 | }) |
34 | 37 | ||
35 | describe('When notifying a user is watching a video', function () { | 38 | describe('When notifying a user is watching a video', function () { |
@@ -106,7 +109,37 @@ describe('Test videos history API validator', function () { | |||
106 | }) | 109 | }) |
107 | }) | 110 | }) |
108 | 111 | ||
109 | describe('When removing user videos history', function () { | 112 | describe('When removing a specific user video history element', function () { |
113 | let path: string | ||
114 | |||
115 | before(function () { | ||
116 | path = myHistoryPath + '/' + videoId | ||
117 | }) | ||
118 | |||
119 | it('Should fail with an unauthenticated user', async function () { | ||
120 | await makeDeleteRequest({ url: server.url, path, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | ||
121 | }) | ||
122 | |||
123 | it('Should fail with a bad videoId parameter', async function () { | ||
124 | await makeDeleteRequest({ | ||
125 | url: server.url, | ||
126 | token: server.accessToken, | ||
127 | path: myHistoryRemove + '/hi', | ||
128 | expectedStatus: HttpStatusCode.BAD_REQUEST_400 | ||
129 | }) | ||
130 | }) | ||
131 | |||
132 | it('Should succeed with the correct parameters', async function () { | ||
133 | await makeDeleteRequest({ | ||
134 | url: server.url, | ||
135 | token: server.accessToken, | ||
136 | path, | ||
137 | expectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
138 | }) | ||
139 | }) | ||
140 | }) | ||
141 | |||
142 | describe('When removing all user videos history', function () { | ||
110 | it('Should fail with an unauthenticated user', async function () { | 143 | it('Should fail with an unauthenticated user', async function () { |
111 | await makePostBodyRequest({ url: server.url, path: myHistoryPath + '/remove', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) | 144 | await makePostBodyRequest({ url: server.url, path: myHistoryPath + '/remove', expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) |
112 | }) | 145 | }) |
diff --git a/server/tests/api/videos/videos-history.ts b/server/tests/api/videos/videos-history.ts index 4e5ba13aa..8648c97f0 100644 --- a/server/tests/api/videos/videos-history.ts +++ b/server/tests/api/videos/videos-history.ts | |||
@@ -17,6 +17,7 @@ const expect = chai.expect | |||
17 | 17 | ||
18 | describe('Test videos history', function () { | 18 | describe('Test videos history', function () { |
19 | let server: PeerTubeServer = null | 19 | let server: PeerTubeServer = null |
20 | let video1Id: number | ||
20 | let video1UUID: string | 21 | let video1UUID: string |
21 | let video2UUID: string | 22 | let video2UUID: string |
22 | let video3UUID: string | 23 | let video3UUID: string |
@@ -34,8 +35,9 @@ describe('Test videos history', function () { | |||
34 | command = server.history | 35 | command = server.history |
35 | 36 | ||
36 | { | 37 | { |
37 | const { uuid } = await server.videos.upload({ attributes: { name: 'video 1' } }) | 38 | const { id, uuid } = await server.videos.upload({ attributes: { name: 'video 1' } }) |
38 | video1UUID = uuid | 39 | video1UUID = uuid |
40 | video1Id = id | ||
39 | } | 41 | } |
40 | 42 | ||
41 | { | 43 | { |
@@ -68,8 +70,8 @@ describe('Test videos history', function () { | |||
68 | }) | 70 | }) |
69 | 71 | ||
70 | it('Should watch the first and second video', async function () { | 72 | it('Should watch the first and second video', async function () { |
71 | await command.wathVideo({ videoId: video2UUID, currentTime: 8 }) | 73 | await command.watchVideo({ videoId: video2UUID, currentTime: 8 }) |
72 | await command.wathVideo({ videoId: video1UUID, currentTime: 3 }) | 74 | await command.watchVideo({ videoId: video1UUID, currentTime: 3 }) |
73 | }) | 75 | }) |
74 | 76 | ||
75 | it('Should return the correct history when listing, searching and getting videos', async function () { | 77 | it('Should return the correct history when listing, searching and getting videos', async function () { |
@@ -122,7 +124,7 @@ describe('Test videos history', function () { | |||
122 | 124 | ||
123 | it('Should have these videos when listing my history', async function () { | 125 | it('Should have these videos when listing my history', async function () { |
124 | video3WatchedDate = new Date() | 126 | video3WatchedDate = new Date() |
125 | await command.wathVideo({ videoId: video3UUID, currentTime: 2 }) | 127 | await command.watchVideo({ videoId: video3UUID, currentTime: 2 }) |
126 | 128 | ||
127 | const body = await command.list() | 129 | const body = await command.list() |
128 | 130 | ||
@@ -150,7 +152,7 @@ describe('Test videos history', function () { | |||
150 | }) | 152 | }) |
151 | 153 | ||
152 | it('Should clear my history', async function () { | 154 | it('Should clear my history', async function () { |
153 | await command.remove({ beforeDate: video3WatchedDate.toISOString() }) | 155 | await command.removeAll({ beforeDate: video3WatchedDate.toISOString() }) |
154 | }) | 156 | }) |
155 | 157 | ||
156 | it('Should have my history cleared', async function () { | 158 | it('Should have my history cleared', async function () { |
@@ -166,7 +168,7 @@ describe('Test videos history', function () { | |||
166 | videosHistoryEnabled: false | 168 | videosHistoryEnabled: false |
167 | }) | 169 | }) |
168 | 170 | ||
169 | await command.wathVideo({ videoId: video2UUID, currentTime: 8, expectedStatus: HttpStatusCode.CONFLICT_409 }) | 171 | await command.watchVideo({ videoId: video2UUID, currentTime: 8, expectedStatus: HttpStatusCode.CONFLICT_409 }) |
170 | }) | 172 | }) |
171 | 173 | ||
172 | it('Should re-enable videos history', async function () { | 174 | it('Should re-enable videos history', async function () { |
@@ -174,7 +176,7 @@ describe('Test videos history', function () { | |||
174 | videosHistoryEnabled: true | 176 | videosHistoryEnabled: true |
175 | }) | 177 | }) |
176 | 178 | ||
177 | await command.wathVideo({ videoId: video1UUID, currentTime: 8 }) | 179 | await command.watchVideo({ videoId: video1UUID, currentTime: 8 }) |
178 | 180 | ||
179 | const body = await command.list() | 181 | const body = await command.list() |
180 | expect(body.total).to.equal(2) | 182 | expect(body.total).to.equal(2) |
@@ -212,6 +214,26 @@ describe('Test videos history', function () { | |||
212 | expect(body.total).to.equal(0) | 214 | expect(body.total).to.equal(0) |
213 | }) | 215 | }) |
214 | 216 | ||
217 | it('Should delete a specific history element', async function () { | ||
218 | { | ||
219 | await command.watchVideo({ videoId: video1UUID, currentTime: 4 }) | ||
220 | await command.watchVideo({ videoId: video2UUID, currentTime: 8 }) | ||
221 | } | ||
222 | |||
223 | { | ||
224 | const body = await command.list() | ||
225 | expect(body.total).to.equal(2) | ||
226 | } | ||
227 | |||
228 | { | ||
229 | await command.removeElement({ videoId: video1Id }) | ||
230 | |||
231 | const body = await command.list() | ||
232 | expect(body.total).to.equal(1) | ||
233 | expect(body.data[0].uuid).to.equal(video2UUID) | ||
234 | } | ||
235 | }) | ||
236 | |||
215 | after(async function () { | 237 | after(async function () { |
216 | await cleanupTests([ server ]) | 238 | await cleanupTests([ server ]) |
217 | }) | 239 | }) |