aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/feeds.ts12
-rw-r--r--server/tests/feeds/feeds.ts109
-rw-r--r--shared/server-commands/feeds/feeds-command.ts21
3 files changed, 97 insertions, 45 deletions
diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts
index 9eb31ed93..241715fb9 100644
--- a/server/controllers/feeds.ts
+++ b/server/controllers/feeds.ts
@@ -312,11 +312,13 @@ function addVideosToFeed (feed: Feed, videos: VideoModel[]) {
312 torrents, 312 torrents,
313 313
314 // Enclosure 314 // Enclosure
315 video: { 315 video: videoFiles.length !== 0
316 url: videoFiles[0].url, 316 ? {
317 length: videoFiles[0].fileSize, 317 url: videoFiles[0].url,
318 type: videoFiles[0].type 318 length: videoFiles[0].fileSize,
319 }, 319 type: videoFiles[0].type
320 }
321 : undefined,
320 322
321 // Media RSS 323 // Media RSS
322 videos: videoFiles, 324 videos: videoFiles,
diff --git a/server/tests/feeds/feeds.ts b/server/tests/feeds/feeds.ts
index 320dc3333..c7331d544 100644
--- a/server/tests/feeds/feeds.ts
+++ b/server/tests/feeds/feeds.ts
@@ -13,6 +13,7 @@ import {
13 PeerTubeServer, 13 PeerTubeServer,
14 setAccessTokensToServers, 14 setAccessTokensToServers,
15 setDefaultChannelAvatar, 15 setDefaultChannelAvatar,
16 stopFfmpeg,
16 waitJobs 17 waitJobs
17} from '@shared/server-commands' 18} from '@shared/server-commands'
18 19
@@ -30,6 +31,7 @@ describe('Test syndication feeds', () => {
30 let userAccountId: number 31 let userAccountId: number
31 let userChannelId: number 32 let userChannelId: number
32 let userFeedToken: string 33 let userFeedToken: string
34 let liveId: string
33 35
34 before(async function () { 36 before(async function () {
35 this.timeout(120000) 37 this.timeout(120000)
@@ -48,6 +50,8 @@ describe('Test syndication feeds', () => {
48 await setDefaultChannelAvatar(servers[0]) 50 await setDefaultChannelAvatar(servers[0])
49 await doubleFollow(servers[0], servers[1]) 51 await doubleFollow(servers[0], servers[1])
50 52
53 await servers[0].config.enableLive({ allowReplay: false, transcoding: false })
54
51 { 55 {
52 const user = await servers[0].users.getMyInfo() 56 const user = await servers[0].users.getMyInfo()
53 rootAccountId = user.account.id 57 rootAccountId = user.account.id
@@ -95,17 +99,17 @@ describe('Test syndication feeds', () => {
95 99
96 it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () { 100 it('Should be well formed XML (covers RSS 2.0 and ATOM 1.0 endpoints)', async function () {
97 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { 101 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) {
98 const rss = await servers[0].feed.getXML({ feed }) 102 const rss = await servers[0].feed.getXML({ feed, ignoreCache: true })
99 expect(rss).xml.to.be.valid() 103 expect(rss).xml.to.be.valid()
100 104
101 const atom = await servers[0].feed.getXML({ feed, format: 'atom' }) 105 const atom = await servers[0].feed.getXML({ feed, format: 'atom', ignoreCache: true })
102 expect(atom).xml.to.be.valid() 106 expect(atom).xml.to.be.valid()
103 } 107 }
104 }) 108 })
105 109
106 it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () { 110 it('Should be well formed JSON (covers JSON feed 1.0 endpoint)', async function () {
107 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) { 111 for (const feed of [ 'video-comments' as 'video-comments', 'videos' as 'videos' ]) {
108 const jsonText = await servers[0].feed.getJSON({ feed }) 112 const jsonText = await servers[0].feed.getJSON({ feed, ignoreCache: true })
109 expect(JSON.parse(jsonText)).to.be.jsonSchema({ type: 'object' }) 113 expect(JSON.parse(jsonText)).to.be.jsonSchema({ type: 'object' })
110 } 114 }
111 }) 115 })
@@ -150,7 +154,7 @@ describe('Test syndication feeds', () => {
150 154
151 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () { 155 it('Should contain a valid enclosure (covers RSS 2.0 endpoint)', async function () {
152 for (const server of servers) { 156 for (const server of servers) {
153 const rss = await server.feed.getXML({ feed: 'videos' }) 157 const rss = await server.feed.getXML({ feed: 'videos', ignoreCache: true })
154 expect(XMLValidator.validate(rss)).to.be.true 158 expect(XMLValidator.validate(rss)).to.be.true
155 159
156 const parser = new XMLParser({ parseAttributeValue: true, ignoreAttributes: false }) 160 const parser = new XMLParser({ parseAttributeValue: true, ignoreAttributes: false })
@@ -167,7 +171,7 @@ describe('Test syndication feeds', () => {
167 171
168 it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () { 172 it('Should contain a valid \'attachments\' object (covers JSON feed 1.0 endpoint)', async function () {
169 for (const server of servers) { 173 for (const server of servers) {
170 const json = await server.feed.getJSON({ feed: 'videos' }) 174 const json = await server.feed.getJSON({ feed: 'videos', ignoreCache: true })
171 const jsonObj = JSON.parse(json) 175 const jsonObj = JSON.parse(json)
172 expect(jsonObj.items.length).to.be.equal(2) 176 expect(jsonObj.items.length).to.be.equal(2)
173 expect(jsonObj.items[0].attachments).to.exist 177 expect(jsonObj.items[0].attachments).to.exist
@@ -180,7 +184,7 @@ describe('Test syndication feeds', () => {
180 184
181 it('Should filter by account', async function () { 185 it('Should filter by account', async function () {
182 { 186 {
183 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: rootAccountId } }) 187 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: rootAccountId }, ignoreCache: true })
184 const jsonObj = JSON.parse(json) 188 const jsonObj = JSON.parse(json)
185 expect(jsonObj.items.length).to.be.equal(1) 189 expect(jsonObj.items.length).to.be.equal(1)
186 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 190 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
@@ -188,7 +192,7 @@ describe('Test syndication feeds', () => {
188 } 192 }
189 193
190 { 194 {
191 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: userAccountId } }) 195 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { accountId: userAccountId }, ignoreCache: true })
192 const jsonObj = JSON.parse(json) 196 const jsonObj = JSON.parse(json)
193 expect(jsonObj.items.length).to.be.equal(1) 197 expect(jsonObj.items.length).to.be.equal(1)
194 expect(jsonObj.items[0].title).to.equal('user video') 198 expect(jsonObj.items[0].title).to.equal('user video')
@@ -197,14 +201,14 @@ describe('Test syndication feeds', () => {
197 201
198 for (const server of servers) { 202 for (const server of servers) {
199 { 203 {
200 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'root@localhost:' + servers[0].port } }) 204 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'root@' + servers[0].host }, ignoreCache: true })
201 const jsonObj = JSON.parse(json) 205 const jsonObj = JSON.parse(json)
202 expect(jsonObj.items.length).to.be.equal(1) 206 expect(jsonObj.items.length).to.be.equal(1)
203 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 207 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
204 } 208 }
205 209
206 { 210 {
207 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'john@localhost:' + servers[0].port } }) 211 const json = await server.feed.getJSON({ feed: 'videos', query: { accountName: 'john@' + servers[0].host }, ignoreCache: true })
208 const jsonObj = JSON.parse(json) 212 const jsonObj = JSON.parse(json)
209 expect(jsonObj.items.length).to.be.equal(1) 213 expect(jsonObj.items.length).to.be.equal(1)
210 expect(jsonObj.items[0].title).to.equal('user video') 214 expect(jsonObj.items[0].title).to.equal('user video')
@@ -214,7 +218,7 @@ describe('Test syndication feeds', () => {
214 218
215 it('Should filter by video channel', async function () { 219 it('Should filter by video channel', async function () {
216 { 220 {
217 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: rootChannelId } }) 221 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: rootChannelId }, ignoreCache: true })
218 const jsonObj = JSON.parse(json) 222 const jsonObj = JSON.parse(json)
219 expect(jsonObj.items.length).to.be.equal(1) 223 expect(jsonObj.items.length).to.be.equal(1)
220 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 224 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
@@ -222,7 +226,7 @@ describe('Test syndication feeds', () => {
222 } 226 }
223 227
224 { 228 {
225 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: userChannelId } }) 229 const json = await servers[0].feed.getJSON({ feed: 'videos', query: { videoChannelId: userChannelId }, ignoreCache: true })
226 const jsonObj = JSON.parse(json) 230 const jsonObj = JSON.parse(json)
227 expect(jsonObj.items.length).to.be.equal(1) 231 expect(jsonObj.items.length).to.be.equal(1)
228 expect(jsonObj.items[0].title).to.equal('user video') 232 expect(jsonObj.items[0].title).to.equal('user video')
@@ -231,16 +235,16 @@ describe('Test syndication feeds', () => {
231 235
232 for (const server of servers) { 236 for (const server of servers) {
233 { 237 {
234 const query = { videoChannelName: 'root_channel@localhost:' + servers[0].port } 238 const query = { videoChannelName: 'root_channel@' + servers[0].host }
235 const json = await server.feed.getJSON({ feed: 'videos', query }) 239 const json = await server.feed.getJSON({ feed: 'videos', query, ignoreCache: true })
236 const jsonObj = JSON.parse(json) 240 const jsonObj = JSON.parse(json)
237 expect(jsonObj.items.length).to.be.equal(1) 241 expect(jsonObj.items.length).to.be.equal(1)
238 expect(jsonObj.items[0].title).to.equal('my super name for server 1') 242 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
239 } 243 }
240 244
241 { 245 {
242 const query = { videoChannelName: 'john_channel@localhost:' + servers[0].port } 246 const query = { videoChannelName: 'john_channel@' + servers[0].host }
243 const json = await server.feed.getJSON({ feed: 'videos', query }) 247 const json = await server.feed.getJSON({ feed: 'videos', query, ignoreCache: true })
244 const jsonObj = JSON.parse(json) 248 const jsonObj = JSON.parse(json)
245 expect(jsonObj.items.length).to.be.equal(1) 249 expect(jsonObj.items.length).to.be.equal(1)
246 expect(jsonObj.items[0].title).to.equal('user video') 250 expect(jsonObj.items[0].title).to.equal('user video')
@@ -255,7 +259,7 @@ describe('Test syndication feeds', () => {
255 259
256 await waitJobs([ serverHLSOnly ]) 260 await waitJobs([ serverHLSOnly ])
257 261
258 const json = await serverHLSOnly.feed.getJSON({ feed: 'videos' }) 262 const json = await serverHLSOnly.feed.getJSON({ feed: 'videos', ignoreCache: true })
259 const jsonObj = JSON.parse(json) 263 const jsonObj = JSON.parse(json)
260 expect(jsonObj.items.length).to.be.equal(1) 264 expect(jsonObj.items.length).to.be.equal(1)
261 expect(jsonObj.items[0].attachments).to.exist 265 expect(jsonObj.items[0].attachments).to.exist
@@ -267,13 +271,48 @@ describe('Test syndication feeds', () => {
267 expect(jsonObj.items[0].attachments[i].url).to.exist 271 expect(jsonObj.items[0].attachments[i].url).to.exist
268 } 272 }
269 }) 273 })
274
275 it('Should not display waiting live videos', async function () {
276 const { uuid } = await servers[0].live.create({
277 fields: {
278 name: 'live',
279 privacy: VideoPrivacy.PUBLIC,
280 channelId: rootChannelId
281 }
282 })
283 liveId = uuid
284
285 const json = await servers[0].feed.getJSON({ feed: 'videos', ignoreCache: true })
286
287 const jsonObj = JSON.parse(json)
288 expect(jsonObj.items.length).to.be.equal(2)
289 expect(jsonObj.items[0].title).to.equal('my super name for server 1')
290 expect(jsonObj.items[1].title).to.equal('user video')
291 })
292
293 it('Should display published live videos', async function () {
294 this.timeout(120000)
295
296 const ffmpeg = await servers[0].live.sendRTMPStreamInVideo({ videoId: liveId, copyCodecs: true, fixtureName: 'video_short.mp4' })
297 await servers[0].live.waitUntilPublished({ videoId: liveId })
298
299 const json = await servers[0].feed.getJSON({ feed: 'videos', ignoreCache: true })
300
301 const jsonObj = JSON.parse(json)
302 expect(jsonObj.items.length).to.be.equal(3)
303 expect(jsonObj.items[0].title).to.equal('live')
304 expect(jsonObj.items[1].title).to.equal('my super name for server 1')
305 expect(jsonObj.items[2].title).to.equal('user video')
306
307 await stopFfmpeg(ffmpeg)
308 })
270 }) 309 })
271 310
272 describe('Video comments feed', function () { 311 describe('Video comments feed', function () {
273 312
274 it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () { 313 it('Should contain valid comments (covers JSON feed 1.0 endpoint) and not from unlisted videos', async function () {
275 for (const server of servers) { 314 for (const server of servers) {
276 const json = await server.feed.getJSON({ feed: 'video-comments' }) 315 const json = await server.feed.getJSON({ feed: 'video-comments', ignoreCache: true })
277 316
278 const jsonObj = JSON.parse(json) 317 const jsonObj = JSON.parse(json)
279 expect(jsonObj.items.length).to.be.equal(2) 318 expect(jsonObj.items.length).to.be.equal(2)
@@ -285,12 +324,12 @@ describe('Test syndication feeds', () => {
285 it('Should not list comments from muted accounts or instances', async function () { 324 it('Should not list comments from muted accounts or instances', async function () {
286 this.timeout(30000) 325 this.timeout(30000)
287 326
288 const remoteHandle = 'root@localhost:' + servers[0].port 327 const remoteHandle = 'root@' + servers[0].host
289 328
290 await servers[1].blocklist.addToServerBlocklist({ account: remoteHandle }) 329 await servers[1].blocklist.addToServerBlocklist({ account: remoteHandle })
291 330
292 { 331 {
293 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 2 } }) 332 const json = await servers[1].feed.getJSON({ feed: 'video-comments', ignoreCache: true })
294 const jsonObj = JSON.parse(json) 333 const jsonObj = JSON.parse(json)
295 expect(jsonObj.items.length).to.be.equal(0) 334 expect(jsonObj.items.length).to.be.equal(0)
296 } 335 }
@@ -303,7 +342,7 @@ describe('Test syndication feeds', () => {
303 await servers[0].comments.createThread({ videoId: videoUUID, text: 'super comment' }) 342 await servers[0].comments.createThread({ videoId: videoUUID, text: 'super comment' })
304 await waitJobs(servers) 343 await waitJobs(servers)
305 344
306 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 3 } }) 345 const json = await servers[1].feed.getJSON({ feed: 'video-comments', ignoreCache: true })
307 const jsonObj = JSON.parse(json) 346 const jsonObj = JSON.parse(json)
308 expect(jsonObj.items.length).to.be.equal(3) 347 expect(jsonObj.items.length).to.be.equal(3)
309 } 348 }
@@ -311,7 +350,7 @@ describe('Test syndication feeds', () => {
311 await servers[1].blocklist.addToMyBlocklist({ account: remoteHandle }) 350 await servers[1].blocklist.addToMyBlocklist({ account: remoteHandle })
312 351
313 { 352 {
314 const json = await servers[1].feed.getJSON({ feed: 'video-comments', query: { version: 4 } }) 353 const json = await servers[1].feed.getJSON({ feed: 'video-comments', ignoreCache: true })
315 const jsonObj = JSON.parse(json) 354 const jsonObj = JSON.parse(json)
316 expect(jsonObj.items.length).to.be.equal(2) 355 expect(jsonObj.items.length).to.be.equal(2)
317 } 356 }
@@ -342,7 +381,7 @@ describe('Test syndication feeds', () => {
342 expect(body.total).to.equal(0) 381 expect(body.total).to.equal(0)
343 382
344 const query = { accountId: feeduserAccountId, token: feeduserFeedToken } 383 const query = { accountId: feeduserAccountId, token: feeduserFeedToken }
345 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query }) 384 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query, ignoreCache: true })
346 const jsonObj = JSON.parse(json) 385 const jsonObj = JSON.parse(json)
347 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos 386 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
348 } 387 }
@@ -350,12 +389,12 @@ describe('Test syndication feeds', () => {
350 389
351 it('Should fail with an invalid token', async function () { 390 it('Should fail with an invalid token', async function () {
352 const query = { accountId: feeduserAccountId, token: 'toto' } 391 const query = { accountId: feeduserAccountId, token: 'toto' }
353 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 392 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403, ignoreCache: true })
354 }) 393 })
355 394
356 it('Should fail with a token of another user', async function () { 395 it('Should fail with a token of another user', async function () {
357 const query = { accountId: feeduserAccountId, token: userFeedToken } 396 const query = { accountId: feeduserAccountId, token: userFeedToken }
358 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 397 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403, ignoreCache: true })
359 }) 398 })
360 399
361 it('Should list no videos for a user with videos but no subscriptions', async function () { 400 it('Should list no videos for a user with videos but no subscriptions', async function () {
@@ -363,7 +402,7 @@ describe('Test syndication feeds', () => {
363 expect(body.total).to.equal(0) 402 expect(body.total).to.equal(0)
364 403
365 const query = { accountId: userAccountId, token: userFeedToken } 404 const query = { accountId: userAccountId, token: userFeedToken }
366 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query }) 405 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query, ignoreCache: true })
367 const jsonObj = JSON.parse(json) 406 const jsonObj = JSON.parse(json)
368 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos 407 expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
369 }) 408 })
@@ -371,7 +410,7 @@ describe('Test syndication feeds', () => {
371 it('Should list self videos for a user with a subscription to themselves', async function () { 410 it('Should list self videos for a user with a subscription to themselves', async function () {
372 this.timeout(30000) 411 this.timeout(30000)
373 412
374 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'john_channel@localhost:' + servers[0].port }) 413 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'john_channel@' + servers[0].host })
375 await waitJobs(servers) 414 await waitJobs(servers)
376 415
377 { 416 {
@@ -379,8 +418,8 @@ describe('Test syndication feeds', () => {
379 expect(body.total).to.equal(1) 418 expect(body.total).to.equal(1)
380 expect(body.data[0].name).to.equal('user video') 419 expect(body.data[0].name).to.equal('user video')
381 420
382 const query = { accountId: userAccountId, token: userFeedToken, version: 1 } 421 const query = { accountId: userAccountId, token: userFeedToken }
383 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query }) 422 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query, ignoreCache: true })
384 const jsonObj = JSON.parse(json) 423 const jsonObj = JSON.parse(json)
385 expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's 424 expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's
386 } 425 }
@@ -389,15 +428,15 @@ describe('Test syndication feeds', () => {
389 it('Should list videos of a user\'s subscription', async function () { 428 it('Should list videos of a user\'s subscription', async function () {
390 this.timeout(30000) 429 this.timeout(30000)
391 430
392 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@localhost:' + servers[0].port }) 431 await servers[0].subscriptions.add({ token: userAccessToken, targetUri: 'root_channel@' + servers[0].host })
393 await waitJobs(servers) 432 await waitJobs(servers)
394 433
395 { 434 {
396 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken }) 435 const body = await servers[0].subscriptions.listVideos({ token: userAccessToken })
397 expect(body.total).to.equal(2, "there should be 2 videos part of the subscription") 436 expect(body.total).to.equal(2, "there should be 2 videos part of the subscription")
398 437
399 const query = { accountId: userAccountId, token: userFeedToken, version: 2 } 438 const query = { accountId: userAccountId, token: userFeedToken }
400 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query }) 439 const json = await servers[0].feed.getJSON({ feed: 'subscriptions', query, ignoreCache: true })
401 const jsonObj = JSON.parse(json) 440 const jsonObj = JSON.parse(json)
402 expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's 441 expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's
403 } 442 }
@@ -406,16 +445,16 @@ describe('Test syndication feeds', () => {
406 it('Should renew the token, and so have an invalid old token', async function () { 445 it('Should renew the token, and so have an invalid old token', async function () {
407 await servers[0].users.renewMyScopedTokens({ token: userAccessToken }) 446 await servers[0].users.renewMyScopedTokens({ token: userAccessToken })
408 447
409 const query = { accountId: userAccountId, token: userFeedToken, version: 3 } 448 const query = { accountId: userAccountId, token: userFeedToken }
410 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 449 await servers[0].feed.getJSON({ feed: 'subscriptions', query, expectedStatus: HttpStatusCode.FORBIDDEN_403, ignoreCache: true })
411 }) 450 })
412 451
413 it('Should succeed with the new token', async function () { 452 it('Should succeed with the new token', async function () {
414 const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken }) 453 const token = await servers[0].users.getMyScopedTokens({ token: userAccessToken })
415 userFeedToken = token.feedToken 454 userFeedToken = token.feedToken
416 455
417 const query = { accountId: userAccountId, token: userFeedToken, version: 4 } 456 const query = { accountId: userAccountId, token: userFeedToken }
418 await servers[0].feed.getJSON({ feed: 'subscriptions', query }) 457 await servers[0].feed.getJSON({ feed: 'subscriptions', query, ignoreCache: true })
419 }) 458 })
420 459
421 }) 460 })
diff --git a/shared/server-commands/feeds/feeds-command.ts b/shared/server-commands/feeds/feeds-command.ts
index 3c95f9536..939b18dee 100644
--- a/shared/server-commands/feeds/feeds-command.ts
+++ b/shared/server-commands/feeds/feeds-command.ts
@@ -1,4 +1,4 @@
1 1import { buildUUID } from '@shared/extra-utils'
2import { HttpStatusCode } from '@shared/models' 2import { HttpStatusCode } from '@shared/models'
3import { AbstractCommand, OverrideCommandOptions } from '../shared' 3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4 4
@@ -8,16 +8,22 @@ export class FeedCommand extends AbstractCommand {
8 8
9 getXML (options: OverrideCommandOptions & { 9 getXML (options: OverrideCommandOptions & {
10 feed: FeedType 10 feed: FeedType
11 ignoreCache: boolean
11 format?: string 12 format?: string
12 }) { 13 }) {
13 const { feed, format } = options 14 const { feed, format, ignoreCache } = options
14 const path = '/feeds/' + feed + '.xml' 15 const path = '/feeds/' + feed + '.xml'
15 16
17 const query: { [id: string]: string } = {}
18
19 if (ignoreCache) query.v = buildUUID()
20 if (format) query.format = format
21
16 return this.getRequestText({ 22 return this.getRequestText({
17 ...options, 23 ...options,
18 24
19 path, 25 path,
20 query: format ? { format } : undefined, 26 query,
21 accept: 'application/xml', 27 accept: 'application/xml',
22 implicitToken: false, 28 implicitToken: false,
23 defaultExpectedStatus: HttpStatusCode.OK_200 29 defaultExpectedStatus: HttpStatusCode.OK_200
@@ -26,16 +32,21 @@ export class FeedCommand extends AbstractCommand {
26 32
27 getJSON (options: OverrideCommandOptions & { 33 getJSON (options: OverrideCommandOptions & {
28 feed: FeedType 34 feed: FeedType
35 ignoreCache: boolean
29 query?: { [ id: string ]: any } 36 query?: { [ id: string ]: any }
30 }) { 37 }) {
31 const { feed, query } = options 38 const { feed, query = {}, ignoreCache } = options
32 const path = '/feeds/' + feed + '.json' 39 const path = '/feeds/' + feed + '.json'
33 40
41 const cacheQuery = ignoreCache
42 ? { v: buildUUID() }
43 : {}
44
34 return this.getRequestText({ 45 return this.getRequestText({
35 ...options, 46 ...options,
36 47
37 path, 48 path,
38 query, 49 query: { ...query, ...cacheQuery },
39 accept: 'application/json', 50 accept: 'application/json',
40 implicitToken: false, 51 implicitToken: false,
41 defaultExpectedStatus: HttpStatusCode.OK_200 52 defaultExpectedStatus: HttpStatusCode.OK_200