From f3aaa9a95cc2b61f1f255472d7014d08faa66561 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 5 Dec 2017 17:46:33 +0100 Subject: [PATCH] Fix client search --- .../account-videos.component.ts | 8 +- client/src/app/app.component.html | 2 +- client/src/app/app.module.ts | 4 +- .../header.component.html} | 2 +- .../header.component.scss} | 0 client/src/app/header/header.component.ts | 28 +++++ client/src/app/header/index.ts | 1 + client/src/app/shared/index.ts | 1 - client/src/app/shared/misc/utils.ts | 18 +++ client/src/app/shared/search/index.ts | 4 - .../app/shared/search/search-field.type.ts | 1 - .../src/app/shared/search/search.component.ts | 42 ------- client/src/app/shared/search/search.model.ts | 6 - .../src/app/shared/search/search.service.ts | 18 --- client/src/app/shared/shared.module.ts | 4 - .../app/shared/video/abstract-video-list.ts | 24 ++-- client/src/app/shared/video/video.service.ts | 9 +- client/src/app/signup/signup.component.html | 2 +- client/src/app/videos/video-list/index.ts | 1 + .../video-recently-added.component.ts | 12 +- .../video-list/video-search.component.ts | 51 ++++++++ .../video-list/video-trending.component.ts | 12 +- .../src/app/videos/videos-routing.module.ts | 11 ++ client/src/app/videos/videos.module.ts | 5 +- server/controllers/api/videos/index.ts | 22 ++-- server/initializers/constants.ts | 6 - server/middlewares/index.ts | 1 - server/middlewares/search.ts | 14 --- server/middlewares/validators/videos.ts | 5 +- server/models/video/video-interface.ts | 1 - server/models/video/video.ts | 47 +++----- server/tests/api/single-server.ts | 112 +++++++++--------- server/tests/utils/videos.ts | 20 ++-- 33 files changed, 245 insertions(+), 249 deletions(-) rename client/src/app/{shared/search/search.component.html => header/header.component.html} (79%) rename client/src/app/{shared/search/search.component.scss => header/header.component.scss} (100%) create mode 100644 client/src/app/header/header.component.ts create mode 100644 client/src/app/header/index.ts create mode 100644 client/src/app/shared/misc/utils.ts delete mode 100644 client/src/app/shared/search/index.ts delete mode 100644 client/src/app/shared/search/search-field.type.ts delete mode 100644 client/src/app/shared/search/search.component.ts delete mode 100644 client/src/app/shared/search/search.model.ts delete mode 100644 client/src/app/shared/search/search.service.ts create mode 100644 client/src/app/videos/video-list/video-search.component.ts delete mode 100644 server/middlewares/search.ts diff --git a/client/src/app/account/account-videos/account-videos.component.ts b/client/src/app/account/account-videos/account-videos.component.ts index cc28f511a..1bc6c0a35 100644 --- a/client/src/app/account/account-videos/account-videos.component.ts +++ b/client/src/app/account/account-videos/account-videos.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' import { AbstractVideoList } from '../../shared/video/abstract-video-list' @@ -9,7 +9,7 @@ import { VideoService } from '../../shared/video/video.service' templateUrl: './account-videos.component.html', styleUrls: [ './account-videos.component.scss' ] }) -export class AccountVideosComponent extends AbstractVideoList implements OnInit, OnDestroy { +export class AccountVideosComponent extends AbstractVideoList implements OnInit { titlePage = 'My videos' currentRoute = '/account/videos' @@ -24,10 +24,6 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit, super.ngOnInit() } - ngOnDestroy () { - super.ngOnDestroy() - } - getVideosObservable () { return this.videoService.getMyVideos(this.pagination, this.sort) } diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index 640524e23..b095e44d6 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html @@ -11,7 +11,7 @@
- +
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 342589003..ee7cb0c8a 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -21,6 +21,7 @@ import { SignupModule } from './signup' import { SharedModule } from './shared' import { VideosModule } from './videos' import { MenuComponent, MenuAdminComponent } from './menu' +import { HeaderComponent } from './header' export function metaFactory (): MetaLoader { return new MetaStaticLoader({ @@ -51,7 +52,8 @@ const APP_PROVIDERS = [ AppComponent, MenuComponent, - MenuAdminComponent + MenuAdminComponent, + HeaderComponent ], imports: [ BrowserModule, diff --git a/client/src/app/shared/search/search.component.html b/client/src/app/header/header.component.html similarity index 79% rename from client/src/app/shared/search/search.component.html rename to client/src/app/header/header.component.html index 9bc9bafe4..aa72fb68a 100644 --- a/client/src/app/shared/search/search.component.html +++ b/client/src/app/header/header.component.html @@ -1,6 +1,6 @@ diff --git a/client/src/app/shared/search/search.component.scss b/client/src/app/header/header.component.scss similarity index 100% rename from client/src/app/shared/search/search.component.scss rename to client/src/app/header/header.component.scss diff --git a/client/src/app/header/header.component.ts b/client/src/app/header/header.component.ts new file mode 100644 index 000000000..a903048f2 --- /dev/null +++ b/client/src/app/header/header.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core' +import { Router } from '@angular/router' +import { getParameterByName } from '../shared/misc/utils' + +@Component({ + selector: 'my-header', + templateUrl: './header.component.html', + styleUrls: [ './header.component.scss' ] +}) + +export class HeaderComponent implements OnInit { + searchValue = '' + + constructor (private router: Router) {} + + ngOnInit () { + const searchQuery = getParameterByName('search', window.location.href) + if (searchQuery) this.searchValue = searchQuery + } + + doSearch () { + if (!this.searchValue) return + + this.router.navigate([ '/videos', 'search' ], { + queryParams: { search: this.searchValue } + }) + } +} diff --git a/client/src/app/header/index.ts b/client/src/app/header/index.ts new file mode 100644 index 000000000..d98d2d00a --- /dev/null +++ b/client/src/app/header/index.ts @@ -0,0 +1 @@ +export * from './header.component' diff --git a/client/src/app/shared/index.ts b/client/src/app/shared/index.ts index 79bf5ef43..413dda16a 100644 --- a/client/src/app/shared/index.ts +++ b/client/src/app/shared/index.ts @@ -1,7 +1,6 @@ export * from './auth' export * from './forms' export * from './rest' -export * from './search' export * from './users' export * from './video-abuse' export * from './video-blacklist' diff --git a/client/src/app/shared/misc/utils.ts b/client/src/app/shared/misc/utils.ts new file mode 100644 index 000000000..2b5c3686e --- /dev/null +++ b/client/src/app/shared/misc/utils.ts @@ -0,0 +1,18 @@ +// Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript + +function getParameterByName (name: string, url: string) { + if (!url) url = window.location.href + name = name.replace(/[\[\]]/g, '\\$&') + + const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)') + const results = regex.exec(url) + + if (!results) return null + if (!results[2]) return '' + + return decodeURIComponent(results[2].replace(/\+/g, ' ')) +} + +export { + getParameterByName +} diff --git a/client/src/app/shared/search/index.ts b/client/src/app/shared/search/index.ts deleted file mode 100644 index d4016cf89..000000000 --- a/client/src/app/shared/search/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './search-field.type' -export * from './search.component' -export * from './search.model' -export * from './search.service' diff --git a/client/src/app/shared/search/search-field.type.ts b/client/src/app/shared/search/search-field.type.ts deleted file mode 100644 index 7323d6cc3..000000000 --- a/client/src/app/shared/search/search-field.type.ts +++ /dev/null @@ -1 +0,0 @@ -export type SearchField = 'name' | 'account' | 'host' | 'tags' diff --git a/client/src/app/shared/search/search.component.ts b/client/src/app/shared/search/search.component.ts deleted file mode 100644 index f49ecc8ad..000000000 --- a/client/src/app/shared/search/search.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, OnInit } from '@angular/core' -import { Router } from '@angular/router' -import { Search } from './search.model' -import { SearchService } from './search.service' - -@Component({ - selector: 'my-search', - templateUrl: './search.component.html', - styleUrls: [ './search.component.scss' ] -}) - -export class SearchComponent implements OnInit { - searchCriteria: Search = { - field: 'name', - value: '' - } - - constructor (private searchService: SearchService, private router: Router) {} - - ngOnInit () { - // Subscribe if the search changed - // Usually changed by videos list component - this.searchService.updateSearch.subscribe( - newSearchCriteria => { - // Put a field by default - if (!newSearchCriteria.field) { - newSearchCriteria.field = 'name' - } - - this.searchCriteria = newSearchCriteria - } - ) - } - - doSearch () { - // if (this.router.url.indexOf('/videos/list') === -1) { - // this.router.navigate([ '/videos/list' ]) - // } - - this.searchService.searchUpdated.next(this.searchCriteria) - } -} diff --git a/client/src/app/shared/search/search.model.ts b/client/src/app/shared/search/search.model.ts deleted file mode 100644 index 174adf2c6..000000000 --- a/client/src/app/shared/search/search.model.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { SearchField } from './search-field.type' - -export interface Search { - field: SearchField - value: string -} diff --git a/client/src/app/shared/search/search.service.ts b/client/src/app/shared/search/search.service.ts deleted file mode 100644 index 0480b46bd..000000000 --- a/client/src/app/shared/search/search.service.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Injectable } from '@angular/core' -import { Subject } from 'rxjs/Subject' -import { ReplaySubject } from 'rxjs/ReplaySubject' - -import { Search } from './search.model' - -// This class is needed to communicate between videos/ and search component -// Remove it when we'll be able to subscribe to router changes -@Injectable() -export class SearchService { - searchUpdated: Subject - updateSearch: Subject - - constructor () { - this.updateSearch = new Subject() - this.searchUpdated = new ReplaySubject(1) - } -} diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index f7ced040d..86e1a380e 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -17,7 +17,6 @@ import { FromNowPipe } from './misc/from-now.pipe' import { LoaderComponent } from './misc/loader.component' import { NumberFormatterPipe } from './misc/number-formatter.pipe' import { RestExtractor, RestService } from './rest' -import { SearchComponent, SearchService } from './search' import { UserService } from './users' import { VideoAbuseService } from './video-abuse' import { VideoBlacklistService } from './video-blacklist' @@ -43,7 +42,6 @@ import { VideoService } from './video/video.service' ], declarations: [ - SearchComponent, LoaderComponent, VideoThumbnailComponent, NumberFormatterPipe, @@ -66,7 +64,6 @@ import { VideoService } from './video/video.service' BytesPipe, KeysPipe, - SearchComponent, LoaderComponent, VideoThumbnailComponent, @@ -78,7 +75,6 @@ import { VideoService } from './video/video.service' AUTH_INTERCEPTOR_PROVIDER, RestExtractor, RestService, - SearchService, VideoAbuseService, VideoBlacklistService, UserService, diff --git a/client/src/app/shared/video/abstract-video-list.ts b/client/src/app/shared/video/abstract-video-list.ts index cf717cf4c..84ca5cbe4 100644 --- a/client/src/app/shared/video/abstract-video-list.ts +++ b/client/src/app/shared/video/abstract-video-list.ts @@ -1,25 +1,25 @@ -import { OnDestroy, OnInit } from '@angular/core' +import { OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' import { Observable } from 'rxjs/Observable' -import { Subscription } from 'rxjs/Subscription' import { SortField } from './sort-field.type' import { VideoPagination } from './video-pagination.model' import { Video } from './video.model' -export abstract class AbstractVideoList implements OnInit, OnDestroy { +export abstract class AbstractVideoList implements OnInit { pagination: VideoPagination = { currentPage: 1, itemsPerPage: 25, totalItems: null } sort: SortField = '-createdAt' + defaultSort: SortField = '-createdAt' videos: Video[] = [] + loadOnInit = true protected notificationsService: NotificationsService protected router: Router protected route: ActivatedRoute - protected subActivatedRoute: Subscription protected abstract currentRoute: string @@ -32,13 +32,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { // Subscribe to route changes const routeParams = this.route.snapshot.params this.loadRouteParams(routeParams) - this.loadMoreVideos('after') - } - - ngOnDestroy () { - if (this.subActivatedRoute) { - this.subActivatedRoute.unsubscribe() - } + if (this.loadOnInit === true) this.loadMoreVideos('after') } onNearOfTop () { @@ -53,6 +47,12 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { } } + reloadVideos () { + this.videos = [] + this.loadedPages = {} + this.loadMoreVideos('before') + } + loadMoreVideos (where: 'before' | 'after') { if (this.loadedPages[this.pagination.currentPage] === true) return @@ -105,7 +105,7 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy { } protected loadRouteParams (routeParams: { [ key: string ]: any }) { - this.sort = routeParams['sort'] as SortField || '-createdAt' + this.sort = routeParams['sort'] as SortField || this.defaultSort if (routeParams['page'] !== undefined) { this.pagination.currentPage = parseInt(routeParams['page'], 10) diff --git a/client/src/app/shared/video/video.service.ts b/client/src/app/shared/video/video.service.ts index b2a26417c..3f35b67c4 100644 --- a/client/src/app/shared/video/video.service.ts +++ b/client/src/app/shared/video/video.service.ts @@ -11,7 +11,7 @@ import { VideoRateType } from '../../../../../shared/models/videos/video-rate.ty import { VideoUpdate } from '../../../../../shared/models/videos/video-update.model' import { RestExtractor } from '../rest/rest-extractor.service' import { RestService } from '../rest/rest.service' -import { Search } from '../search/search.model' +import { Search } from '../header/search.model' import { UserService } from '../users/user.service' import { SortField } from './sort-field.type' import { VideoDetails } from './video-details.model' @@ -91,15 +91,14 @@ export class VideoService { .catch((res) => this.restExtractor.handleError(res)) } - searchVideos (search: Search, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { - const url = VideoService.BASE_VIDEO_URL + 'search/' + encodeURIComponent(search.value) + searchVideos (search: string, videoPagination: VideoPagination, sort: SortField): Observable<{ videos: Video[], totalVideos: number}> { + const url = VideoService.BASE_VIDEO_URL + 'search' const pagination = this.videoPaginationToRestPagination(videoPagination) let params = new HttpParams() params = this.restService.addRestGetParams(params, pagination, sort) - - if (search.field) params.set('field', search.field) + params = params.append('search', search) return this.authHttp .get>(url, { params }) diff --git a/client/src/app/signup/signup.component.html b/client/src/app/signup/signup.component.html index 1e9f7f949..8a30ab512 100644 --- a/client/src/app/signup/signup.component.html +++ b/client/src/app/signup/signup.component.html @@ -1,7 +1,7 @@
- Signup + Create an account
{{ error }}
diff --git a/client/src/app/videos/video-list/index.ts b/client/src/app/videos/video-list/index.ts index 594e31984..13024294e 100644 --- a/client/src/app/videos/video-list/index.ts +++ b/client/src/app/videos/video-list/index.ts @@ -1,3 +1,4 @@ export * from './video-recently-added.component' export * from './video-trending.component' +export * from './video-search.component' export * from './shared' diff --git a/client/src/app/videos/video-list/video-recently-added.component.ts b/client/src/app/videos/video-list/video-recently-added.component.ts index d48804414..6168fac95 100644 --- a/client/src/app/videos/video-list/video-recently-added.component.ts +++ b/client/src/app/videos/video-list/video-recently-added.component.ts @@ -1,17 +1,19 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from '../../shared/video/video.service' import { AbstractVideoList } from '../../shared/video/abstract-video-list' +import { SortField } from '../../shared/video/sort-field.type' +import { VideoService } from '../../shared/video/video.service' @Component({ selector: 'my-videos-recently-added', styleUrls: [ '../../shared/video/abstract-video-list.scss' ], templateUrl: '../../shared/video/abstract-video-list.html' }) -export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit, OnDestroy { +export class VideoRecentlyAddedComponent extends AbstractVideoList implements OnInit { titlePage = 'Recently added' currentRoute = '/videos/recently-added' + sort: SortField = '-createdAt' constructor (protected router: Router, protected route: ActivatedRoute, @@ -24,10 +26,6 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On super.ngOnInit() } - ngOnDestroy () { - super.ngOnDestroy() - } - getVideosObservable () { return this.videoService.getVideos(this.pagination, this.sort) } diff --git a/client/src/app/videos/video-list/video-search.component.ts b/client/src/app/videos/video-list/video-search.component.ts new file mode 100644 index 000000000..ba851d27e --- /dev/null +++ b/client/src/app/videos/video-list/video-search.component.ts @@ -0,0 +1,51 @@ +import { Component, OnDestroy, OnInit } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { NotificationsService } from 'angular2-notifications' +import { AbstractVideoList } from 'app/shared/video/abstract-video-list' +import { Subscription } from 'rxjs/Subscription' +import { SortField } from '../../shared/video/sort-field.type' +import { VideoService } from '../../shared/video/video.service' + +@Component({ + selector: 'my-videos-search', + styleUrls: [ '../../shared/video/abstract-video-list.scss' ], + templateUrl: '../../shared/video/abstract-video-list.html' +}) +export class VideoSearchComponent extends AbstractVideoList implements OnInit, OnDestroy { + titlePage = 'Search' + currentRoute = '/videos/search' + loadOnInit = false + + private search = '' + private subActivatedRoute: Subscription + + constructor (protected router: Router, + protected route: ActivatedRoute, + protected notificationsService: NotificationsService, + private videoService: VideoService) { + super() + } + + ngOnInit () { + super.ngOnInit() + + this.subActivatedRoute = this.route.queryParams.subscribe( + queryParams => { + this.search = queryParams['search'] + this.reloadVideos() + }, + + err => this.notificationsService.error('Error', err.text) + ) + } + + ngOnDestroy () { + if (this.subActivatedRoute) { + this.subActivatedRoute.unsubscribe() + } + } + + getVideosObservable () { + return this.videoService.searchVideos(this.search, this.pagination, this.sort) + } +} diff --git a/client/src/app/videos/video-list/video-trending.component.ts b/client/src/app/videos/video-list/video-trending.component.ts index 9108289c9..e80fd7f2c 100644 --- a/client/src/app/videos/video-list/video-trending.component.ts +++ b/client/src/app/videos/video-list/video-trending.component.ts @@ -1,17 +1,19 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, OnInit } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { NotificationsService } from 'angular2-notifications' -import { VideoService } from '../../shared/video/video.service' import { AbstractVideoList } from 'app/shared/video/abstract-video-list' +import { SortField } from '../../shared/video/sort-field.type' +import { VideoService } from '../../shared/video/video.service' @Component({ selector: 'my-videos-trending', styleUrls: [ '../../shared/video/abstract-video-list.scss' ], templateUrl: '../../shared/video/abstract-video-list.html' }) -export class VideoTrendingComponent extends AbstractVideoList implements OnInit, OnDestroy { +export class VideoTrendingComponent extends AbstractVideoList implements OnInit { titlePage = 'Trending' currentRoute = '/videos/trending' + defaultSort: SortField = '-views' constructor (protected router: Router, protected route: ActivatedRoute, @@ -24,10 +26,6 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit, super.ngOnInit() } - ngOnDestroy () { - super.ngOnDestroy() - } - getVideosObservable () { return this.videoService.getVideos(this.pagination, this.sort) } diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts index 204851c81..6910421b7 100644 --- a/client/src/app/videos/videos-routing.module.ts +++ b/client/src/app/videos/videos-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' import { MetaGuard } from '@ngx-meta/core' +import { VideoSearchComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosComponent } from './videos.component' @@ -34,6 +35,15 @@ const videosRoutes: Routes = [ } } }, + { + path: 'search', + component: VideoSearchComponent, + data: { + meta: { + title: 'Search videos' + } + } + }, { path: 'upload', loadChildren: 'app/videos/+video-edit#VideoAddModule', @@ -54,6 +64,7 @@ const videosRoutes: Routes = [ }, { path: ':uuid', + pathMatch: 'full', redirectTo: 'watch/:uuid' }, { diff --git a/client/src/app/videos/videos.module.ts b/client/src/app/videos/videos.module.ts index 6d846fd3b..8c8d52ad9 100644 --- a/client/src/app/videos/videos.module.ts +++ b/client/src/app/videos/videos.module.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core' import { SharedModule } from '../shared' -import { VideoMiniatureComponent } from './video-list' +import { VideoMiniatureComponent, VideoSearchComponent } from './video-list' import { VideoRecentlyAddedComponent } from './video-list/video-recently-added.component' import { VideoTrendingComponent } from './video-list/video-trending.component' import { VideosRoutingModule } from './videos-routing.module' @@ -17,7 +17,8 @@ import { VideosComponent } from './videos.component' VideoTrendingComponent, VideoRecentlyAddedComponent, - VideoMiniatureComponent + VideoMiniatureComponent, + VideoSearchComponent ], exports: [ diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index e2798830e..2b70d535e 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -26,7 +26,6 @@ import { authenticate, paginationValidator, setPagination, - setVideosSearch, setVideosSort, videosAddValidator, videosGetValidator, @@ -84,6 +83,14 @@ videosRouter.get('/', setPagination, asyncMiddleware(listVideos) ) +videosRouter.get('/search', + videosSearchValidator, + paginationValidator, + videosSortValidator, + setVideosSort, + setPagination, + asyncMiddleware(searchVideos) +) videosRouter.put('/:id', authenticate, asyncMiddleware(videosUpdateValidator), @@ -115,16 +122,6 @@ videosRouter.delete('/:id', asyncMiddleware(removeVideoRetryWrapper) ) -videosRouter.get('/search/:value', - videosSearchValidator, - paginationValidator, - videosSortValidator, - setVideosSort, - setPagination, - setVideosSearch, - asyncMiddleware(searchVideos) -) - // --------------------------------------------------------------------------- export { @@ -378,8 +375,7 @@ async function removeVideo (req: express.Request, res: express.Response) { async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) { const resultList = await db.Video.searchAndPopulateAccountAndServerAndTags( - req.params.value, - req.query.field, + req.query.search, req.query.start, req.query.count, req.query.sort diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 144a4edbf..3e083fd92 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -24,11 +24,6 @@ const API_VERSION = 'v1' // Number of results by default for the pagination const PAGINATION_COUNT_DEFAULT = 15 -// Sortable columns per schema -const SEARCHABLE_COLUMNS = { - VIDEOS: [ 'name', 'magnetUri', 'host', 'account', 'tags' ] -} - // Sortable columns per schema const SORTABLE_COLUMNS = { USERS: [ 'id', 'username', 'createdAt' ], @@ -361,7 +356,6 @@ export { REMOTE_SCHEME, FOLLOW_STATES, AVATARS_DIR, - SEARCHABLE_COLUMNS, SERVER_ACCOUNT_NAME, PRIVATE_RSA_KEY_SIZE, SORTABLE_COLUMNS, diff --git a/server/middlewares/index.ts b/server/middlewares/index.ts index aafcad2d9..0cef26953 100644 --- a/server/middlewares/index.ts +++ b/server/middlewares/index.ts @@ -4,6 +4,5 @@ export * from './async' export * from './oauth' export * from './pagination' export * from './servers' -export * from './search' export * from './sort' export * from './user-right' diff --git a/server/middlewares/search.ts b/server/middlewares/search.ts deleted file mode 100644 index 6fe83d25b..000000000 --- a/server/middlewares/search.ts +++ /dev/null @@ -1,14 +0,0 @@ -import 'express-validator' -import * as express from 'express' - -function setVideosSearch (req: express.Request, res: express.Response, next: express.NextFunction) { - if (!req.query.field) req.query.field = 'name' - - return next() -} - -// --------------------------------------------------------------------------- - -export { - setVideosSearch -} diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index f21680aa0..ee2ac50c8 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts @@ -18,7 +18,7 @@ import { } from '../../helpers/custom-validators/videos' import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' import { logger } from '../../helpers/logger' -import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' +import { CONSTRAINTS_FIELDS } from '../../initializers' import { database as db } from '../../initializers/database' import { UserInstance } from '../../models/account/user-interface' import { VideoInstance } from '../../models/video/video-interface' @@ -172,8 +172,7 @@ const videosRemoveValidator = [ ] const videosSearchValidator = [ - param('value').not().isEmpty().withMessage('Should have a valid search'), - query('field').optional().isIn(SEARCHABLE_COLUMNS.VIDEOS).withMessage('Should have correct searchable column'), + query('search').not().isEmpty().withMessage('Should have a valid search'), (req: express.Request, res: express.Response, next: express.NextFunction) => { logger.debug('Checking videosSearch parameters', { parameters: req.params }) diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts index be140de86..2a63350af 100644 --- a/server/models/video/video-interface.ts +++ b/server/models/video/video-interface.ts @@ -50,7 +50,6 @@ export namespace VideoMethods { export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Bluebird< ResultList > export type SearchAndPopulateAccountAndServerAndTags = ( value: string, - field: string, start: number, count: number, sort: string diff --git a/server/models/video/video.ts b/server/models/video/video.ts index f3469c1de..4dce8e2fc 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -1070,7 +1070,7 @@ loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) { return Video.findOne(options) } -searchAndPopulateAccountAndServerAndTags = function (value: string, field: string, start: number, count: number, sort: string) { +searchAndPopulateAccountAndServerAndTags = function (value: string, start: number, count: number, sort: string) { const serverInclude: Sequelize.IncludeOptions = { model: Video['sequelize'].models.Server, required: false @@ -1099,33 +1099,24 @@ searchAndPopulateAccountAndServerAndTags = function (value: string, field: strin order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ] } - if (field === 'tags') { - const escapedValue = Video['sequelize'].escape('%' + value + '%') - query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal( - `(SELECT "VideoTags"."videoId" - FROM "Tags" - INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" - WHERE name ILIKE ${escapedValue} - )` - ) - } else if (field === 'host') { - // FIXME: Include our server? (not stored in the database) - serverInclude.where = { - host: { - [Sequelize.Op.iLike]: '%' + value + '%' - } - } - serverInclude.required = true - } else if (field === 'account') { - accountInclude.where = { - name: { - [Sequelize.Op.iLike]: '%' + value + '%' - } - } - } else { - query.where[field] = { - [Sequelize.Op.iLike]: '%' + value + '%' - } + // TODO: search on tags too + // const escapedValue = Video['sequelize'].escape('%' + value + '%') + // query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal( + // `(SELECT "VideoTags"."videoId" + // FROM "Tags" + // INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" + // WHERE name ILIKE ${escapedValue} + // )` + // ) + + // TODO: search on account too + // accountInclude.where = { + // name: { + // [Sequelize.Op.iLike]: '%' + value + '%' + // } + // } + query.where['name'] = { + [Sequelize.Op.iLike]: '%' + value + '%' } query.include = [ diff --git a/server/tests/api/single-server.ts b/server/tests/api/single-server.ts index 041d13225..fe192d391 100644 --- a/server/tests/api/single-server.ts +++ b/server/tests/api/single-server.ts @@ -225,7 +225,7 @@ describe('Test a single server', function () { expect(video.views).to.equal(3) }) - it('Should search the video by name by default', async function () { + it('Should search the video by name', async function () { const res = await searchVideo(server.url, 'my') expect(res.body.total).to.equal(1) @@ -279,35 +279,36 @@ describe('Test a single server', function () { // }) // }) - it('Should search the video by tag', async function () { - const res = await searchVideo(server.url, 'tag1', 'tags') - - expect(res.body.total).to.equal(1) - expect(res.body.data).to.be.an('array') - expect(res.body.data.length).to.equal(1) - - const video = res.body.data[0] - expect(video.name).to.equal('my super name') - expect(video.category).to.equal(2) - expect(video.categoryLabel).to.equal('Films') - expect(video.licence).to.equal(6) - expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives') - expect(video.language).to.equal(3) - expect(video.languageLabel).to.equal('Mandarin') - expect(video.nsfw).to.be.ok - expect(video.description).to.equal('my super description') - expect(video.serverHost).to.equal('localhost:9001') - expect(video.account).to.equal('root') - expect(video.isLocal).to.be.true - expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) - expect(dateIsValid(video.createdAt)).to.be.true - expect(dateIsValid(video.updatedAt)).to.be.true - - const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) - expect(test).to.equal(true) - }) + // Not implemented yet + // it('Should search the video by tag', async function () { + // const res = await searchVideo(server.url, 'tag1') + // + // expect(res.body.total).to.equal(1) + // expect(res.body.data).to.be.an('array') + // expect(res.body.data.length).to.equal(1) + // + // const video = res.body.data[0] + // expect(video.name).to.equal('my super name') + // expect(video.category).to.equal(2) + // expect(video.categoryLabel).to.equal('Films') + // expect(video.licence).to.equal(6) + // expect(video.licenceLabel).to.equal('Attribution - Non Commercial - No Derivatives') + // expect(video.language).to.equal(3) + // expect(video.languageLabel).to.equal('Mandarin') + // expect(video.nsfw).to.be.ok + // expect(video.description).to.equal('my super description') + // expect(video.serverHost).to.equal('localhost:9001') + // expect(video.account).to.equal('root') + // expect(video.isLocal).to.be.true + // expect(video.tags).to.deep.equal([ 'tag1', 'tag2', 'tag3' ]) + // expect(dateIsValid(video.createdAt)).to.be.true + // expect(dateIsValid(video.updatedAt)).to.be.true + // + // const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) + // expect(test).to.equal(true) + // }) - it('Should not find a search by name by default', async function () { + it('Should not find a search by name', async function () { const res = await searchVideo(server.url, 'hello') expect(res.body.total).to.equal(0) @@ -315,21 +316,23 @@ describe('Test a single server', function () { expect(res.body.data.length).to.equal(0) }) - it('Should not find a search by author', async function () { - const res = await searchVideo(server.url, 'hello', 'account') - - expect(res.body.total).to.equal(0) - expect(res.body.data).to.be.an('array') - expect(res.body.data.length).to.equal(0) - }) - - it('Should not find a search by tag', async function () { - const res = await searchVideo(server.url, 'hello', 'tags') - - expect(res.body.total).to.equal(0) - expect(res.body.data).to.be.an('array') - expect(res.body.data.length).to.equal(0) - }) + // Not implemented yet + // it('Should not find a search by author', async function () { + // const res = await searchVideo(server.url, 'hello') + // + // expect(res.body.total).to.equal(0) + // expect(res.body.data).to.be.an('array') + // expect(res.body.data.length).to.equal(0) + // }) + // + // Not implemented yet + // it('Should not find a search by tag', async function () { + // const res = await searchVideo(server.url, 'hello') + // + // expect(res.body.total).to.equal(0) + // expect(res.body.data).to.be.an('array') + // expect(res.body.data.length).to.equal(0) + // }) it('Should remove the video', async function () { await removeVideo(server.url, server.accessToken, videoId) @@ -443,7 +446,7 @@ describe('Test a single server', function () { }) it('Should search the first video', async function () { - const res = await searchVideoWithPagination(server.url, 'webm', 'name', 0, 1, 'name') + const res = await searchVideoWithPagination(server.url, 'webm', 0, 1, 'name') const videos = res.body.data expect(res.body.total).to.equal(4) @@ -452,7 +455,7 @@ describe('Test a single server', function () { }) it('Should search the last two videos', async function () { - const res = await searchVideoWithPagination(server.url, 'webm', 'name', 2, 2, 'name') + const res = await searchVideoWithPagination(server.url, 'webm', 2, 2, 'name') const videos = res.body.data expect(res.body.total).to.equal(4) @@ -462,20 +465,21 @@ describe('Test a single server', function () { }) it('Should search all the webm videos', async function () { - const res = await searchVideoWithPagination(server.url, 'webm', 'name', 0, 15) + const res = await searchVideoWithPagination(server.url, 'webm', 0, 15) const videos = res.body.data expect(res.body.total).to.equal(4) expect(videos.length).to.equal(4) }) - it('Should search all the root author videos', async function () { - const res = await searchVideoWithPagination(server.url, 'root', 'account', 0, 15) - - const videos = res.body.data - expect(res.body.total).to.equal(6) - expect(videos.length).to.equal(6) - }) + // Not implemented yet + // it('Should search all the root author videos', async function () { + // const res = await searchVideoWithPagination(server.url, 'root', 0, 15) + // + // const videos = res.body.data + // expect(res.body.total).to.equal(6) + // expect(videos.length).to.equal(6) + // }) // Not implemented yet // it('Should search all the 9001 port videos', async function () { diff --git a/server/tests/utils/videos.ts b/server/tests/utils/videos.ts index 73a9f1a0a..ff7da9bb2 100644 --- a/server/tests/utils/videos.ts +++ b/server/tests/utils/videos.ts @@ -145,26 +145,25 @@ function removeVideo (url: string, token: string, id: number, expectedStatus = 2 .expect(expectedStatus) } -function searchVideo (url: string, search: string, field?: string) { +function searchVideo (url: string, search: string) { const path = '/api/v1/videos' const req = request(url) - .get(path + '/search/' + search) - .set('Accept', 'application/json') - - if (field) req.query({ field }) + .get(path + '/search') + .query({ search }) + .set('Accept', 'application/json') return req.expect(200) - .expect('Content-Type', /json/) + .expect('Content-Type', /json/) } -function searchVideoWithPagination (url: string, search: string, field: string, start: number, count: number, sort?: string) { +function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) { const path = '/api/v1/videos' const req = request(url) - .get(path + '/search/' + search) + .get(path + '/search') .query({ start }) + .query({ search }) .query({ count }) - .query({ field }) if (sort) req.query({ sort }) @@ -177,7 +176,8 @@ function searchVideoWithSort (url: string, search: string, sort: string) { const path = '/api/v1/videos' return request(url) - .get(path + '/search/' + search) + .get(path + '/search') + .query({ search }) .query({ sort }) .set('Accept', 'application/json') .expect(200) -- 2.41.0