aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-07-29 10:27:24 +0200
committerChocobozzz <me@florianbigard.com>2021-07-29 10:27:24 +0200
commitb033851fb54241bb703f86add025229e68cc6f59 (patch)
treed60dac1499a95bf9dc902dee24f325fe0f8b6acb
parentfbd67e7f386504e50f2504cb6386700a58906f16 (diff)
downloadPeerTube-b033851fb54241bb703f86add025229e68cc6f59.tar.gz
PeerTube-b033851fb54241bb703f86add025229e68cc6f59.tar.zst
PeerTube-b033851fb54241bb703f86add025229e68cc6f59.zip
Search channels against handles and not names
-rw-r--r--server/controllers/api/search/search-video-channels.ts2
-rw-r--r--server/helpers/custom-validators/misc.ts5
-rw-r--r--server/middlewares/validators/search.ts7
-rw-r--r--server/models/video/video-channel.ts36
-rw-r--r--server/tests/api/check-params/search.ts4
-rw-r--r--server/tests/api/search/search-channels.ts17
-rw-r--r--shared/core-utils/index.ts1
-rw-r--r--shared/core-utils/utils/index.ts1
-rw-r--r--shared/core-utils/utils/object.ts15
-rw-r--r--shared/extra-utils/moderation/abuses-command.ts6
-rw-r--r--shared/extra-utils/server/follows-command.ts8
-rw-r--r--shared/extra-utils/server/jobs-command.ts2
-rw-r--r--shared/extra-utils/users/users-command.ts3
-rw-r--r--shared/extra-utils/videos/channels-command.ts2
-rw-r--r--shared/extra-utils/videos/playlists-command.ts3
-rw-r--r--shared/extra-utils/videos/videos-command.ts3
-rw-r--r--shared/models/search/video-channels-search-query.model.ts2
17 files changed, 85 insertions, 32 deletions
diff --git a/server/controllers/api/search/search-video-channels.ts b/server/controllers/api/search/search-video-channels.ts
index 9fc2d53a5..ae32a6726 100644
--- a/server/controllers/api/search/search-video-channels.ts
+++ b/server/controllers/api/search/search-video-channels.ts
@@ -100,7 +100,7 @@ async function searchVideoChannelsDB (query: VideoChannelsSearchQuery, res: expr
100 count: query.count, 100 count: query.count,
101 sort: query.sort, 101 sort: query.sort,
102 host: query.host, 102 host: query.host,
103 names: query.names 103 handles: query.handles
104 }, 'filter:api.search.video-channels.local.list.params') 104 }, 'filter:api.search.video-channels.local.list.params')
105 105
106 const resultList = await Hooks.wrapPromiseFun( 106 const resultList = await Hooks.wrapPromiseFun(
diff --git a/server/helpers/custom-validators/misc.ts b/server/helpers/custom-validators/misc.ts
index f8f168149..c19a3e5eb 100644
--- a/server/helpers/custom-validators/misc.ts
+++ b/server/helpers/custom-validators/misc.ts
@@ -23,6 +23,10 @@ function isNotEmptyIntArray (value: any) {
23 return Array.isArray(value) && value.every(v => validator.isInt('' + v)) && value.length !== 0 23 return Array.isArray(value) && value.every(v => validator.isInt('' + v)) && value.length !== 0
24} 24}
25 25
26function isNotEmptyStringArray (value: any) {
27 return Array.isArray(value) && value.every(v => typeof v === 'string' && v.length !== 0) && value.length !== 0
28}
29
26function isArrayOf (value: any, validator: (value: any) => boolean) { 30function isArrayOf (value: any, validator: (value: any) => boolean) {
27 return isArray(value) && value.every(v => validator(v)) 31 return isArray(value) && value.every(v => validator(v))
28} 32}
@@ -187,6 +191,7 @@ export {
187 isIntOrNull, 191 isIntOrNull,
188 isIdValid, 192 isIdValid,
189 isSafePath, 193 isSafePath,
194 isNotEmptyStringArray,
190 isUUIDValid, 195 isUUIDValid,
191 toCompleteUUIDs, 196 toCompleteUUIDs,
192 toCompleteUUID, 197 toCompleteUUID,
diff --git a/server/middlewares/validators/search.ts b/server/middlewares/validators/search.ts
index cde300968..27d0e541d 100644
--- a/server/middlewares/validators/search.ts
+++ b/server/middlewares/validators/search.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import { query } from 'express-validator' 2import { query } from 'express-validator'
3import { isSearchTargetValid } from '@server/helpers/custom-validators/search' 3import { isSearchTargetValid } from '@server/helpers/custom-validators/search'
4import { isHostValid } from '@server/helpers/custom-validators/servers' 4import { isHostValid } from '@server/helpers/custom-validators/servers'
5import { areUUIDsValid, isDateValid, toCompleteUUIDs } from '../../helpers/custom-validators/misc' 5import { areUUIDsValid, isDateValid, isNotEmptyStringArray, toCompleteUUIDs } from '../../helpers/custom-validators/misc'
6import { logger } from '../../helpers/logger' 6import { logger } from '../../helpers/logger'
7import { areValidationErrors } from './shared' 7import { areValidationErrors } from './shared'
8 8
@@ -64,9 +64,10 @@ const videoChannelsListSearchValidator = [
64 .optional() 64 .optional()
65 .custom(isSearchTargetValid).withMessage('Should have a valid search target'), 65 .custom(isSearchTargetValid).withMessage('Should have a valid search target'),
66 66
67 query('names') 67 query('handles')
68 .optional() 68 .optional()
69 .toArray(), 69 .toArray()
70 .custom(isNotEmptyStringArray).withMessage('Should have valid handles'),
70 71
71 (req: express.Request, res: express.Response, next: express.NextFunction) => { 72 (req: express.Request, res: express.Response, next: express.NextFunction) => {
72 logger.debug('Checking video channels search query', { parameters: req.query }) 73 logger.debug('Checking video channels search query', { parameters: req.query })
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 327f49304..e4b12c517 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -18,7 +18,7 @@ import {
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { MAccountActor } from '@server/types/models' 20import { MAccountActor } from '@server/types/models'
21import { AttributesOnly } from '@shared/core-utils' 21import { AttributesOnly, pick } from '@shared/core-utils'
22import { ActivityPubActor } from '../../../shared/models/activitypub' 22import { ActivityPubActor } from '../../../shared/models/activitypub'
23import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' 23import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos'
24import { 24import {
@@ -59,7 +59,7 @@ type AvailableForListOptions = {
59 actorId: number 59 actorId: number
60 search?: string 60 search?: string
61 host?: string 61 host?: string
62 names?: string[] 62 handles?: string[]
63} 63}
64 64
65type AvailableWithStatsOptions = { 65type AvailableWithStatsOptions = {
@@ -114,15 +114,33 @@ export type SummaryOptions = {
114 }) 114 })
115 } 115 }
116 116
117 if (options.names) { 117 let rootWhere: WhereOptions
118 whereActorAnd.push({ 118 if (options.handles) {
119 preferredUsername: { 119 const or: WhereOptions[] = []
120 [Op.in]: options.names 120
121 for (const handle of options.handles || []) {
122 const [ preferredUsername, host ] = handle.split('@')
123
124 if (!host) {
125 or.push({
126 '$Actor.preferredUsername$': preferredUsername,
127 '$Actor.serverId$': null
128 })
129 } else {
130 or.push({
131 '$Actor.preferredUsername$': preferredUsername,
132 '$Actor.Server.host$': host
133 })
121 } 134 }
122 }) 135 }
136
137 rootWhere = {
138 [Op.or]: or
139 }
123 } 140 }
124 141
125 return { 142 return {
143 where: rootWhere,
126 include: [ 144 include: [
127 { 145 {
128 attributes: { 146 attributes: {
@@ -473,7 +491,7 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
473 sort: string 491 sort: string
474 492
475 host?: string 493 host?: string
476 names?: string[] 494 handles?: string[]
477 }) { 495 }) {
478 let attributesInclude: any[] = [ literal('0 as similarity') ] 496 let attributesInclude: any[] = [ literal('0 as similarity') ]
479 let where: WhereOptions 497 let where: WhereOptions
@@ -507,7 +525,7 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
507 525
508 return VideoChannelModel 526 return VideoChannelModel
509 .scope({ 527 .scope({
510 method: [ ScopeNames.FOR_API, { actorId: options.actorId, host: options.host, names: options.names } as AvailableForListOptions ] 528 method: [ ScopeNames.FOR_API, pick(options, [ 'actorId', 'host', 'handles' ]) as AvailableForListOptions ]
511 }) 529 })
512 .findAndCountAll(query) 530 .findAndCountAll(query)
513 .then(({ rows, count }) => { 531 .then(({ rows, count }) => {
diff --git a/server/tests/api/check-params/search.ts b/server/tests/api/check-params/search.ts
index 789ea7754..cc15d2593 100644
--- a/server/tests/api/check-params/search.ts
+++ b/server/tests/api/check-params/search.ts
@@ -216,6 +216,10 @@ describe('Test videos API validator', function () {
216 await makeGetRequest({ url: server.url, path, query: { ...query, host: '6565' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 216 await makeGetRequest({ url: server.url, path, query: { ...query, host: '6565' }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
217 }) 217 })
218 218
219 it('Should fail with invalid handles', async function () {
220 await makeGetRequest({ url: server.url, path, query: { ...query, handles: [ '' ] }, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
221 })
222
219 it('Should succeed with the correct parameters', async function () { 223 it('Should succeed with the correct parameters', async function () {
220 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 }) 224 await makeGetRequest({ url: server.url, path, query, expectedStatus: HttpStatusCode.OK_200 })
221 }) 225 })
diff --git a/server/tests/api/search/search-channels.ts b/server/tests/api/search/search-channels.ts
index ef78c0f67..4485c424e 100644
--- a/server/tests/api/search/search-channels.ts
+++ b/server/tests/api/search/search-channels.ts
@@ -122,18 +122,25 @@ describe('Test channels search', function () {
122 122
123 it('Should filter by names', async function () { 123 it('Should filter by names', async function () {
124 { 124 {
125 const body = await command.advancedChannelSearch({ search: { names: [ 'squall_channel', 'zell_channel' ] } }) 125 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel', 'zell_channel' ] } })
126 expect(body.total).to.equal(2) 126 expect(body.total).to.equal(1)
127 expect(body.data).to.have.lengthOf(2) 127 expect(body.data).to.have.lengthOf(1)
128 expect(body.data[0].displayName).to.equal('Squall channel') 128 expect(body.data[0].displayName).to.equal('Squall channel')
129 expect(body.data[1].displayName).to.equal('Zell channel')
130 } 129 }
131 130
132 { 131 {
133 const body = await command.advancedChannelSearch({ search: { names: [ 'chocobozzz_channel' ] } }) 132 const body = await command.advancedChannelSearch({ search: { handles: [ 'chocobozzz_channel' ] } })
134 expect(body.total).to.equal(0) 133 expect(body.total).to.equal(0)
135 expect(body.data).to.have.lengthOf(0) 134 expect(body.data).to.have.lengthOf(0)
136 } 135 }
136
137 {
138 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel', 'zell_channel@' + remoteServer.host ] } })
139 expect(body.total).to.equal(2)
140 expect(body.data).to.have.lengthOf(2)
141 expect(body.data[0].displayName).to.equal('Squall channel')
142 expect(body.data[1].displayName).to.equal('Zell channel')
143 }
137 }) 144 })
138 145
139 after(async function () { 146 after(async function () {
diff --git a/shared/core-utils/index.ts b/shared/core-utils/index.ts
index 66d50ef93..2a7d4d982 100644
--- a/shared/core-utils/index.ts
+++ b/shared/core-utils/index.ts
@@ -4,3 +4,4 @@ export * from './i18n'
4export * from './plugins' 4export * from './plugins'
5export * from './renderer' 5export * from './renderer'
6export * from './users' 6export * from './users'
7export * from './utils'
diff --git a/shared/core-utils/utils/index.ts b/shared/core-utils/utils/index.ts
new file mode 100644
index 000000000..a71977d88
--- /dev/null
+++ b/shared/core-utils/utils/index.ts
@@ -0,0 +1 @@
export * from './object'
diff --git a/shared/core-utils/utils/object.ts b/shared/core-utils/utils/object.ts
new file mode 100644
index 000000000..7b2bb81d0
--- /dev/null
+++ b/shared/core-utils/utils/object.ts
@@ -0,0 +1,15 @@
1function pick <T extends object> (object: T, keys: (keyof T)[]) {
2 const result: Partial<T> = {}
3
4 for (const key of keys) {
5 if (Object.prototype.hasOwnProperty.call(object, key)) {
6 result[key] = object[key]
7 }
8 }
9
10 return result
11}
12
13export {
14 pick
15}
diff --git a/shared/extra-utils/moderation/abuses-command.ts b/shared/extra-utils/moderation/abuses-command.ts
index 7b3abb056..0db32ba46 100644
--- a/shared/extra-utils/moderation/abuses-command.ts
+++ b/shared/extra-utils/moderation/abuses-command.ts
@@ -1,4 +1,4 @@
1import { pick } from 'lodash' 1import { pick } from '@shared/core-utils'
2import { 2import {
3 AbuseFilter, 3 AbuseFilter,
4 AbuseMessage, 4 AbuseMessage,
@@ -81,7 +81,7 @@ export class AbusesCommand extends AbstractCommand {
81 searchVideo?: string 81 searchVideo?: string
82 searchVideoChannel?: string 82 searchVideoChannel?: string
83 } = {}) { 83 } = {}) {
84 const toPick = [ 84 const toPick: (keyof typeof options)[] = [
85 'count', 85 'count',
86 'filter', 86 'filter',
87 'id', 87 'id',
@@ -121,7 +121,7 @@ export class AbusesCommand extends AbstractCommand {
121 search?: string 121 search?: string
122 state?: AbuseState 122 state?: AbuseState
123 }) { 123 }) {
124 const toPick = [ 124 const toPick: (keyof typeof options)[] = [
125 'id', 125 'id',
126 'search', 126 'search',
127 'state', 127 'state',
diff --git a/shared/extra-utils/server/follows-command.ts b/shared/extra-utils/server/follows-command.ts
index 2b889cf66..01ef6f179 100644
--- a/shared/extra-utils/server/follows-command.ts
+++ b/shared/extra-utils/server/follows-command.ts
@@ -1,4 +1,4 @@
1import { pick } from 'lodash' 1import { pick } from '@shared/core-utils'
2import { ActivityPubActorType, ActorFollow, FollowState, HttpStatusCode, ResultList, ServerFollowCreate } from '@shared/models' 2import { ActivityPubActorType, ActorFollow, FollowState, HttpStatusCode, ResultList, ServerFollowCreate } from '@shared/models'
3import { AbstractCommand, OverrideCommandOptions } from '../shared' 3import { AbstractCommand, OverrideCommandOptions } from '../shared'
4import { PeerTubeServer } from './server' 4import { PeerTubeServer } from './server'
@@ -15,8 +15,7 @@ export class FollowsCommand extends AbstractCommand {
15 }) { 15 }) {
16 const path = '/api/v1/server/followers' 16 const path = '/api/v1/server/followers'
17 17
18 const toPick = [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ] 18 const query = pick(options, [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ])
19 const query = pick(options, toPick)
20 19
21 return this.getRequestBody<ResultList<ActorFollow>>({ 20 return this.getRequestBody<ResultList<ActorFollow>>({
22 ...options, 21 ...options,
@@ -38,8 +37,7 @@ export class FollowsCommand extends AbstractCommand {
38 } = {}) { 37 } = {}) {
39 const path = '/api/v1/server/following' 38 const path = '/api/v1/server/following'
40 39
41 const toPick = [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ] 40 const query = pick(options, [ 'start', 'count', 'sort', 'search', 'state', 'actorType' ])
42 const query = pick(options, toPick)
43 41
44 return this.getRequestBody<ResultList<ActorFollow>>({ 42 return this.getRequestBody<ResultList<ActorFollow>>({
45 ...options, 43 ...options,
diff --git a/shared/extra-utils/server/jobs-command.ts b/shared/extra-utils/server/jobs-command.ts
index 09a299e5b..c4eb12dc2 100644
--- a/shared/extra-utils/server/jobs-command.ts
+++ b/shared/extra-utils/server/jobs-command.ts
@@ -1,4 +1,4 @@
1import { pick } from 'lodash' 1import { pick } from '@shared/core-utils'
2import { HttpStatusCode } from '@shared/models' 2import { HttpStatusCode } from '@shared/models'
3import { Job, JobState, JobType, ResultList } from '../../models' 3import { Job, JobState, JobType, ResultList } from '../../models'
4import { AbstractCommand, OverrideCommandOptions } from '../shared' 4import { AbstractCommand, OverrideCommandOptions } from '../shared'
diff --git a/shared/extra-utils/users/users-command.ts b/shared/extra-utils/users/users-command.ts
index d66ad15f2..ddd20d041 100644
--- a/shared/extra-utils/users/users-command.ts
+++ b/shared/extra-utils/users/users-command.ts
@@ -1,4 +1,5 @@
1import { omit, pick } from 'lodash' 1import { omit } from 'lodash'
2import { pick } from '@shared/core-utils'
2import { 3import {
3 HttpStatusCode, 4 HttpStatusCode,
4 MyUser, 5 MyUser,
diff --git a/shared/extra-utils/videos/channels-command.ts b/shared/extra-utils/videos/channels-command.ts
index f8eb3f885..255e1d62d 100644
--- a/shared/extra-utils/videos/channels-command.ts
+++ b/shared/extra-utils/videos/channels-command.ts
@@ -1,4 +1,4 @@
1import { pick } from 'lodash' 1import { pick } from '@shared/core-utils'
2import { HttpStatusCode, ResultList, VideoChannel, VideoChannelCreateResult } from '@shared/models' 2import { HttpStatusCode, ResultList, VideoChannel, VideoChannelCreateResult } from '@shared/models'
3import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model' 3import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model'
4import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model' 4import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-update.model'
diff --git a/shared/extra-utils/videos/playlists-command.ts b/shared/extra-utils/videos/playlists-command.ts
index 6f329800e..ce23900d3 100644
--- a/shared/extra-utils/videos/playlists-command.ts
+++ b/shared/extra-utils/videos/playlists-command.ts
@@ -1,4 +1,5 @@
1import { omit, pick } from 'lodash' 1import { omit } from 'lodash'
2import { pick } from '@shared/core-utils'
2import { 3import {
3 BooleanBothQuery, 4 BooleanBothQuery,
4 HttpStatusCode, 5 HttpStatusCode,
diff --git a/shared/extra-utils/videos/videos-command.ts b/shared/extra-utils/videos/videos-command.ts
index 98465e8f6..33725bfdc 100644
--- a/shared/extra-utils/videos/videos-command.ts
+++ b/shared/extra-utils/videos/videos-command.ts
@@ -3,10 +3,11 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { createReadStream, stat } from 'fs-extra' 4import { createReadStream, stat } from 'fs-extra'
5import got, { Response as GotResponse } from 'got' 5import got, { Response as GotResponse } from 'got'
6import { omit, pick } from 'lodash' 6import { omit } from 'lodash'
7import validator from 'validator' 7import validator from 'validator'
8import { buildUUID } from '@server/helpers/uuid' 8import { buildUUID } from '@server/helpers/uuid'
9import { loadLanguages } from '@server/initializers/constants' 9import { loadLanguages } from '@server/initializers/constants'
10import { pick } from '@shared/core-utils'
10import { 11import {
11 HttpStatusCode, 12 HttpStatusCode,
12 ResultList, 13 ResultList,
diff --git a/shared/models/search/video-channels-search-query.model.ts b/shared/models/search/video-channels-search-query.model.ts
index 50c59d41d..77cea4a59 100644
--- a/shared/models/search/video-channels-search-query.model.ts
+++ b/shared/models/search/video-channels-search-query.model.ts
@@ -8,5 +8,5 @@ export interface VideoChannelsSearchQuery extends SearchTargetQuery {
8 sort?: string 8 sort?: string
9 9
10 host?: string 10 host?: string
11 names?: string[] 11 handles?: string[]
12} 12}