diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/search/search-video-channels.ts | 2 | ||||
-rw-r--r-- | server/helpers/custom-validators/misc.ts | 5 | ||||
-rw-r--r-- | server/middlewares/validators/search.ts | 7 | ||||
-rw-r--r-- | server/models/video/video-channel.ts | 36 | ||||
-rw-r--r-- | server/tests/api/check-params/search.ts | 4 | ||||
-rw-r--r-- | server/tests/api/search/search-channels.ts | 17 |
6 files changed, 53 insertions, 18 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 | ||
26 | function isNotEmptyStringArray (value: any) { | ||
27 | return Array.isArray(value) && value.every(v => typeof v === 'string' && v.length !== 0) && value.length !== 0 | ||
28 | } | ||
29 | |||
26 | function isArrayOf (value: any, validator: (value: any) => boolean) { | 30 | function 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' | |||
2 | import { query } from 'express-validator' | 2 | import { query } from 'express-validator' |
3 | import { isSearchTargetValid } from '@server/helpers/custom-validators/search' | 3 | import { isSearchTargetValid } from '@server/helpers/custom-validators/search' |
4 | import { isHostValid } from '@server/helpers/custom-validators/servers' | 4 | import { isHostValid } from '@server/helpers/custom-validators/servers' |
5 | import { areUUIDsValid, isDateValid, toCompleteUUIDs } from '../../helpers/custom-validators/misc' | 5 | import { areUUIDsValid, isDateValid, isNotEmptyStringArray, toCompleteUUIDs } from '../../helpers/custom-validators/misc' |
6 | import { logger } from '../../helpers/logger' | 6 | import { logger } from '../../helpers/logger' |
7 | import { areValidationErrors } from './shared' | 7 | import { 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' |
20 | import { MAccountActor } from '@server/types/models' | 20 | import { MAccountActor } from '@server/types/models' |
21 | import { AttributesOnly } from '@shared/core-utils' | 21 | import { AttributesOnly, pick } from '@shared/core-utils' |
22 | import { ActivityPubActor } from '../../../shared/models/activitypub' | 22 | import { ActivityPubActor } from '../../../shared/models/activitypub' |
23 | import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' | 23 | import { VideoChannel, VideoChannelSummary } from '../../../shared/models/videos' |
24 | import { | 24 | import { |
@@ -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 | ||
65 | type AvailableWithStatsOptions = { | 65 | type 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 () { |