aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-01-03 14:17:57 +0100
committerChocobozzz <me@florianbigard.com>2020-01-03 14:17:57 +0100
commitac0868bcc0259d4ff14265d9ae403e10869a13aa (patch)
tree7a86bc8f4d881360d5b613dc906be1a0d99aa0df
parent35f28e94c763370616d25d5820f4b9ef70cedca9 (diff)
downloadPeerTube-ac0868bcc0259d4ff14265d9ae403e10869a13aa.tar.gz
PeerTube-ac0868bcc0259d4ff14265d9ae403e10869a13aa.tar.zst
PeerTube-ac0868bcc0259d4ff14265d9ae403e10869a13aa.zip
Improve SQL query for my special playlists
-rw-r--r--server/controllers/api/users/me.ts7
-rw-r--r--server/models/account/user.ts72
-rw-r--r--server/tests/api/users/users.ts5
-rw-r--r--server/typings/models/user/user.ts10
-rw-r--r--shared/models/users/user.model.ts11
5 files changed, 63 insertions, 42 deletions
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index 2f3efe6aa..ac7c62aab 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -126,14 +126,13 @@ async function getUserVideoImports (req: express.Request, res: express.Response)
126 126
127async function getUserInformation (req: express.Request, res: express.Response) { 127async function getUserInformation (req: express.Request, res: express.Response) {
128 // We did not load channels in res.locals.user 128 // We did not load channels in res.locals.user
129 const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username) 129 const user = await UserModel.loadForMeAPI(res.locals.oauth.token.user.username)
130 130
131 return res.json(user.toFormattedJSON({ me: true })) 131 return res.json(user.toMeFormattedJSON())
132} 132}
133 133
134async function getUserVideoQuotaUsed (req: express.Request, res: express.Response) { 134async function getUserVideoQuotaUsed (req: express.Request, res: express.Response) {
135 // We did not load channels in res.locals.user 135 const user = res.locals.oauth.token.user
136 const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
137 const videoQuotaUsed = await UserModel.getOriginalVideoFileTotalFromUser(user) 136 const videoQuotaUsed = await UserModel.getOriginalVideoFileTotalFromUser(user)
138 const videoQuotaUsedDaily = await UserModel.getOriginalVideoFileTotalDailyFromUser(user) 137 const videoQuotaUsedDaily = await UserModel.getOriginalVideoFileTotalDailyFromUser(user)
139 138
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 8bd41de22..27262af42 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -19,14 +19,15 @@ import {
19 Table, 19 Table,
20 UpdatedAt 20 UpdatedAt
21} from 'sequelize-typescript' 21} from 'sequelize-typescript'
22import { hasUserRight, USER_ROLE_LABELS, UserRight, VideoPrivacy, MyUser } from '../../../shared' 22import { hasUserRight, MyUser, USER_ROLE_LABELS, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared'
23import { User, UserRole } from '../../../shared/models/users' 23import { User, UserRole } from '../../../shared/models/users'
24import { 24import {
25 isNoInstanceConfigWarningModal, 25 isNoInstanceConfigWarningModal,
26 isNoWelcomeModal,
26 isUserAdminFlagsValid, 27 isUserAdminFlagsValid,
27 isUserAutoPlayVideoValid,
28 isUserAutoPlayNextVideoValid,
29 isUserAutoPlayNextVideoPlaylistValid, 28 isUserAutoPlayNextVideoPlaylistValid,
29 isUserAutoPlayNextVideoValid,
30 isUserAutoPlayVideoValid,
30 isUserBlockedReasonValid, 31 isUserBlockedReasonValid,
31 isUserBlockedValid, 32 isUserBlockedValid,
32 isUserEmailVerifiedValid, 33 isUserEmailVerifiedValid,
@@ -38,8 +39,7 @@ import {
38 isUserVideoQuotaDailyValid, 39 isUserVideoQuotaDailyValid,
39 isUserVideoQuotaValid, 40 isUserVideoQuotaValid,
40 isUserVideosHistoryEnabledValid, 41 isUserVideosHistoryEnabledValid,
41 isUserWebTorrentEnabledValid, 42 isUserWebTorrentEnabledValid
42 isNoWelcomeModal
43} from '../../helpers/custom-validators/users' 43} from '../../helpers/custom-validators/users'
44import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' 44import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
45import { OAuthTokenModel } from '../oauth/oauth-token' 45import { OAuthTokenModel } from '../oauth/oauth-token'
@@ -61,16 +61,17 @@ import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
61import { getThemeOrDefault } from '../../lib/plugins/theme-utils' 61import { getThemeOrDefault } from '../../lib/plugins/theme-utils'
62import * as Bluebird from 'bluebird' 62import * as Bluebird from 'bluebird'
63import { 63import {
64 MMyUserFormattable,
64 MUserDefault, 65 MUserDefault,
65 MUserFormattable, 66 MUserFormattable,
66 MUserId, 67 MUserId,
67 MUserNotifSettingChannelDefault, 68 MUserNotifSettingChannelDefault,
68 MUserWithNotificationSetting, MVideoFullLight 69 MUserWithNotificationSetting,
70 MVideoFullLight
69} from '@server/typings/models' 71} from '@server/typings/models'
70 72
71enum ScopeNames { 73enum ScopeNames {
72 WITH_VIDEO_CHANNEL = 'WITH_VIDEO_CHANNEL', 74 FOR_ME_API = 'FOR_ME_API'
73 WITH_SPECIAL_PLAYLISTS = 'WITH_SPECIAL_PLAYLISTS'
74} 75}
75 76
76@DefaultScope(() => ({ 77@DefaultScope(() => ({
@@ -86,28 +87,31 @@ enum ScopeNames {
86 ] 87 ]
87})) 88}))
88@Scopes(() => ({ 89@Scopes(() => ({
89 [ScopeNames.WITH_VIDEO_CHANNEL]: { 90 [ScopeNames.FOR_ME_API]: {
90 include: [ 91 include: [
91 { 92 {
92 model: AccountModel, 93 model: AccountModel,
93 required: true, 94 include: [
94 include: [ VideoChannelModel ] 95 {
96 model: VideoChannelModel
97 },
98 {
99 attributes: [ 'id', 'name', 'type' ],
100 model: VideoPlaylistModel.unscoped(),
101 required: true,
102 where: {
103 type: {
104 [ Op.ne ]: VideoPlaylistType.REGULAR
105 }
106 }
107 }
108 ]
95 }, 109 },
96 { 110 {
97 model: UserNotificationSettingModel, 111 model: UserNotificationSettingModel,
98 required: true 112 required: true
99 } 113 }
100 ] 114 ]
101 },
102 [ScopeNames.WITH_SPECIAL_PLAYLISTS]: {
103 attributes: {
104 include: [
105 [
106 literal('(select array(select "id" from "videoPlaylist" where "ownerAccountId" in (select id from public.account where "userId" = "UserModel"."id") and name LIKE \'Watch later\'))'),
107 'specialPlaylists'
108 ]
109 ]
110 }
111 } 115 }
112})) 116}))
113@Table({ 117@Table({
@@ -436,17 +440,14 @@ export class UserModel extends Model<UserModel> {
436 return UserModel.findOne(query) 440 return UserModel.findOne(query)
437 } 441 }
438 442
439 static loadByUsernameAndPopulateChannels (username: string): Bluebird<MUserNotifSettingChannelDefault> { 443 static loadForMeAPI (username: string): Bluebird<MUserNotifSettingChannelDefault> {
440 const query = { 444 const query = {
441 where: { 445 where: {
442 username: { [ Op.iLike ]: username } 446 username: { [ Op.iLike ]: username }
443 } 447 }
444 } 448 }
445 449
446 return UserModel.scope([ 450 return UserModel.scope(ScopeNames.FOR_ME_API).findOne(query)
447 ScopeNames.WITH_VIDEO_CHANNEL,
448 ScopeNames.WITH_SPECIAL_PLAYLISTS
449 ]).findOne(query)
450 } 451 }
451 452
452 static loadByEmail (email: string): Bluebird<MUserDefault> { 453 static loadByEmail (email: string): Bluebird<MUserDefault> {
@@ -625,11 +626,11 @@ export class UserModel extends Model<UserModel> {
625 return comparePassword(password, this.password) 626 return comparePassword(password, this.password)
626 } 627 }
627 628
628 toFormattedJSON (this: MUserFormattable, parameters: { withAdminFlags?: boolean, me?: boolean } = {}): User | MyUser { 629 toFormattedJSON (this: MUserFormattable, parameters: { withAdminFlags?: boolean } = {}): User {
629 const videoQuotaUsed = this.get('videoQuotaUsed') 630 const videoQuotaUsed = this.get('videoQuotaUsed')
630 const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') 631 const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily')
631 632
632 const json: User | MyUser = { 633 const json: User = {
633 id: this.id, 634 id: this.id,
634 username: this.username, 635 username: this.username,
635 email: this.email, 636 email: this.email,
@@ -690,15 +691,18 @@ export class UserModel extends Model<UserModel> {
690 }) 691 })
691 } 692 }
692 693
693 if (parameters.me) {
694 Object.assign(json, {
695 specialPlaylists: (this.get('specialPlaylists') as Array<number>).map(p => ({ id: p }))
696 })
697 }
698
699 return json 694 return json
700 } 695 }
701 696
697 toMeFormattedJSON (this: MMyUserFormattable): MyUser {
698 const formatted = this.toFormattedJSON()
699
700 const specialPlaylists = this.Account.VideoPlaylists
701 .map(p => ({ id: p.id, name: p.name, type: p.type }))
702
703 return Object.assign(formatted, { specialPlaylists })
704 }
705
702 async isAbleToUploadVideo (videoFile: { size: number }) { 706 async isAbleToUploadVideo (videoFile: { size: number }) {
703 if (this.videoQuota === -1 && this.videoQuotaDaily === -1) return Promise.resolve(true) 707 if (this.videoQuota === -1 && this.videoQuotaDaily === -1) return Promise.resolve(true)
704 708
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index 3c3ee3ed7..24203a731 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -2,7 +2,7 @@
2 2
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { User, UserRole, Video, MyUser } from '../../../../shared/index' 5import { User, UserRole, Video, MyUser, VideoPlaylistType } from '../../../../shared/index'
6import { 6import {
7 blockUser, 7 blockUser,
8 cleanupTests, 8 cleanupTests,
@@ -251,7 +251,7 @@ describe('Test users', function () {
251 251
252 it('Should be able to get user information', async function () { 252 it('Should be able to get user information', async function () {
253 const res1 = await getMyUserInformation(server.url, accessTokenUser) 253 const res1 = await getMyUserInformation(server.url, accessTokenUser)
254 const userMe: User & MyUser = res1.body 254 const userMe: MyUser = res1.body
255 255
256 const res2 = await getUserInformation(server.url, server.accessToken, userMe.id) 256 const res2 = await getUserInformation(server.url, server.accessToken, userMe.id)
257 const userGet: User = res2.body 257 const userGet: User = res2.body
@@ -271,6 +271,7 @@ describe('Test users', function () {
271 expect(userGet.adminFlags).to.equal(UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST) 271 expect(userGet.adminFlags).to.equal(UserAdminFlag.BY_PASS_VIDEO_AUTO_BLACKLIST)
272 272
273 expect(userMe.specialPlaylists).to.have.lengthOf(1) 273 expect(userMe.specialPlaylists).to.have.lengthOf(1)
274 expect(userMe.specialPlaylists[0].type).to.equal(VideoPlaylistType.WATCH_LATER)
274 }) 275 })
275 }) 276 })
276 277
diff --git a/server/typings/models/user/user.ts b/server/typings/models/user/user.ts
index a2750adc7..6ac19c20b 100644
--- a/server/typings/models/user/user.ts
+++ b/server/typings/models/user/user.ts
@@ -12,6 +12,7 @@ import {
12import { MNotificationSetting, MNotificationSettingFormattable } from './user-notification-setting' 12import { MNotificationSetting, MNotificationSettingFormattable } from './user-notification-setting'
13import { AccountModel } from '@server/models/account/account' 13import { AccountModel } from '@server/models/account/account'
14import { MChannelFormattable } from '../video/video-channels' 14import { MChannelFormattable } from '../video/video-channels'
15import { MVideoPlaylist } from '@server/typings/models'
15 16
16type Use<K extends keyof UserModel, M> = PickWith<UserModel, K, M> 17type Use<K extends keyof UserModel, M> = PickWith<UserModel, K, M>
17 18
@@ -65,6 +66,13 @@ export type MUserDefault = MUser &
65 66
66// Format for API or AP object 67// Format for API or AP object
67 68
69type MAccountWithChannels = MAccountFormattable & PickWithOpt<AccountModel, 'VideoChannels', MChannelFormattable[]>
70type MAccountWithChannelsAndSpecialPlaylists = MAccountWithChannels &
71 PickWithOpt<AccountModel, 'VideoPlaylists', MVideoPlaylist[]>
72
68export type MUserFormattable = MUserQuotaUsed & 73export type MUserFormattable = MUserQuotaUsed &
69 Use<'Account', MAccountFormattable & PickWithOpt<AccountModel, 'VideoChannels', MChannelFormattable[]>> & 74 Use<'Account', MAccountWithChannels> &
70 PickWithOpt<UserModel, 'NotificationSetting', MNotificationSettingFormattable> 75 PickWithOpt<UserModel, 'NotificationSetting', MNotificationSettingFormattable>
76
77export type MMyUserFormattable = MUserFormattable &
78 Use<'Account', MAccountWithChannelsAndSpecialPlaylists>
diff --git a/shared/models/users/user.model.ts b/shared/models/users/user.model.ts
index 1434dca81..328b69df6 100644
--- a/shared/models/users/user.model.ts
+++ b/shared/models/users/user.model.ts
@@ -5,6 +5,7 @@ import { UserRole } from './user-role'
5import { NSFWPolicyType } from '../videos/nsfw-policy.type' 5import { NSFWPolicyType } from '../videos/nsfw-policy.type'
6import { UserNotificationSetting } from './user-notification-setting.model' 6import { UserNotificationSetting } from './user-notification-setting.model'
7import { UserAdminFlag } from './user-flag.model' 7import { UserAdminFlag } from './user-flag.model'
8import { VideoPlaylistType } from '@shared/models'
8 9
9export interface User { 10export interface User {
10 id: number 11 id: number
@@ -47,6 +48,14 @@ export interface User {
47 createdAt: Date 48 createdAt: Date
48} 49}
49 50
51export interface MyUserSpecialPlaylist {
52 id: number
53 name: string
54 type: VideoPlaylistType
55}
56
50export interface MyUser extends User { 57export interface MyUser extends User {
51 specialPlaylists: Partial<VideoPlaylist>[] 58 specialPlaylists: MyUserSpecialPlaylist[]
52} 59}
60
61