Add like/dislike system for videos
This commit is contained in:
parent
8f90644321
commit
d38b828106
|
@ -8,7 +8,7 @@ import { RestExtractor } from '../rest';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
private static BASE_USERS_URL = '/api/v1/users/';
|
static BASE_USERS_URL = '/api/v1/users/';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private authHttp: AuthHttp,
|
private authHttp: AuthHttp,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './loader';
|
export * from './loader';
|
||||||
export * from './sort-field.type';
|
export * from './sort-field.type';
|
||||||
|
export * from './rate-type.type';
|
||||||
export * from './video.model';
|
export * from './video.model';
|
||||||
export * from './video.service';
|
export * from './video.service';
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export type RateType = 'like' | 'dislike';
|
|
@ -1,3 +1,3 @@
|
||||||
export type SortField = "name" | "-name"
|
export type SortField = 'name' | '-name'
|
||||||
| "duration" | "-duration"
|
| 'duration' | '-duration'
|
||||||
| "createdAt" | "-createdAt";
|
| 'createdAt' | '-createdAt';
|
||||||
|
|
|
@ -12,6 +12,8 @@ export class Video {
|
||||||
tags: string[];
|
tags: string[];
|
||||||
thumbnailPath: string;
|
thumbnailPath: string;
|
||||||
views: number;
|
views: number;
|
||||||
|
likes: number;
|
||||||
|
dislikes: number;
|
||||||
|
|
||||||
private static createByString(author: string, podHost: string) {
|
private static createByString(author: string, podHost: string) {
|
||||||
return author + '@' + podHost;
|
return author + '@' + podHost;
|
||||||
|
@ -38,7 +40,9 @@ export class Video {
|
||||||
podHost: string,
|
podHost: string,
|
||||||
tags: string[],
|
tags: string[],
|
||||||
thumbnailPath: string,
|
thumbnailPath: string,
|
||||||
views: number
|
views: number,
|
||||||
|
likes: number,
|
||||||
|
dislikes: number,
|
||||||
}) {
|
}) {
|
||||||
this.author = hash.author;
|
this.author = hash.author;
|
||||||
this.createdAt = new Date(hash.createdAt);
|
this.createdAt = new Date(hash.createdAt);
|
||||||
|
@ -52,6 +56,8 @@ export class Video {
|
||||||
this.tags = hash.tags;
|
this.tags = hash.tags;
|
||||||
this.thumbnailPath = hash.thumbnailPath;
|
this.thumbnailPath = hash.thumbnailPath;
|
||||||
this.views = hash.views;
|
this.views = hash.views;
|
||||||
|
this.likes = hash.likes;
|
||||||
|
this.dislikes = hash.dislikes;
|
||||||
|
|
||||||
this.by = Video.createByString(hash.author, hash.podHost);
|
this.by = Video.createByString(hash.author, hash.podHost);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,16 @@ import 'rxjs/add/operator/map';
|
||||||
|
|
||||||
import { Search } from '../../shared';
|
import { Search } from '../../shared';
|
||||||
import { SortField } from './sort-field.type';
|
import { SortField } from './sort-field.type';
|
||||||
|
import { RateType } from './rate-type.type';
|
||||||
import { AuthService } from '../../core';
|
import { AuthService } from '../../core';
|
||||||
import { AuthHttp, RestExtractor, RestPagination, RestService, ResultList } from '../../shared';
|
import {
|
||||||
|
AuthHttp,
|
||||||
|
RestExtractor,
|
||||||
|
RestPagination,
|
||||||
|
RestService,
|
||||||
|
ResultList,
|
||||||
|
UserService
|
||||||
|
} from '../../shared';
|
||||||
import { Video } from './video.model';
|
import { Video } from './video.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -56,16 +64,43 @@ export class VideoService {
|
||||||
}
|
}
|
||||||
|
|
||||||
reportVideo(id: string, reason: string) {
|
reportVideo(id: string, reason: string) {
|
||||||
|
const url = VideoService.BASE_VIDEO_URL + id + '/abuse';
|
||||||
const body = {
|
const body = {
|
||||||
reason
|
reason
|
||||||
};
|
};
|
||||||
const url = VideoService.BASE_VIDEO_URL + id + '/abuse';
|
|
||||||
|
|
||||||
return this.authHttp.post(url, body)
|
return this.authHttp.post(url, body)
|
||||||
.map(this.restExtractor.extractDataBool)
|
.map(this.restExtractor.extractDataBool)
|
||||||
.catch((res) => this.restExtractor.handleError(res));
|
.catch((res) => this.restExtractor.handleError(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setVideoLike(id: string) {
|
||||||
|
return this.setVideoRate(id, 'like');
|
||||||
|
}
|
||||||
|
|
||||||
|
setVideoDislike(id: string) {
|
||||||
|
return this.setVideoRate(id, 'dislike');
|
||||||
|
}
|
||||||
|
|
||||||
|
getUserVideoRating(id: string) {
|
||||||
|
const url = UserService.BASE_USERS_URL + '/me/videos/' + id + '/rating';
|
||||||
|
|
||||||
|
return this.authHttp.get(url)
|
||||||
|
.map(this.restExtractor.extractDataGet)
|
||||||
|
.catch((res) => this.restExtractor.handleError(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
private setVideoRate(id: string, rateType: RateType) {
|
||||||
|
const url = VideoService.BASE_VIDEO_URL + id + '/rate';
|
||||||
|
const body = {
|
||||||
|
rating: rateType
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.authHttp.put(url, body)
|
||||||
|
.map(this.restExtractor.extractDataBool)
|
||||||
|
.catch((res) => this.restExtractor.handleError(res));
|
||||||
|
}
|
||||||
|
|
||||||
private extractVideos(result: ResultList) {
|
private extractVideos(result: ResultList) {
|
||||||
const videosJson = result.data;
|
const videosJson = result.data;
|
||||||
const totalVideos = result.total;
|
const totalVideos = result.total;
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
<div *ngIf="video !== null" id="video-info">
|
<div *ngIf="video !== null" id="video-info">
|
||||||
<div class="row" id="video-name-actions">
|
<div class="row" id="video-name-actions">
|
||||||
<div class="col-md-8">
|
<div class="col-md-6">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div id="video-name" class="col-md-12">
|
<div id="video-name" class="col-md-12">
|
||||||
{{ video.name }}
|
{{ video.name }}
|
||||||
|
@ -52,7 +52,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="video-actions" class="col-md-4 text-right">
|
<div id="video-actions" class="col-md-6 text-right">
|
||||||
|
<div id="rates">
|
||||||
|
<button
|
||||||
|
id="likes" class="btn btn-default"
|
||||||
|
[ngClass]="{ 'not-interactive-btn': !isUserLoggedIn(), 'activated-btn': userRating === 'like' }" (click)="setLike()"
|
||||||
|
>
|
||||||
|
<span class="glyphicon glyphicon-thumbs-up"></span> {{ video.likes }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="dislikes" class="btn btn-default"
|
||||||
|
[ngClass]="{ 'not-interactive-btn': !isUserLoggedIn(), 'activated-btn': userRating === 'dislike' }" (click)="setDislike()"
|
||||||
|
>
|
||||||
|
<span class=" glyphicon glyphicon-thumbs-down"></span> {{ video.dislikes }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button id="share" class="btn btn-default" (click)="showShareModal()">
|
<button id="share" class="btn btn-default" (click)="showShareModal()">
|
||||||
<span class="glyphicon glyphicon-share"></span> Share
|
<span class="glyphicon glyphicon-share"></span> Share
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -47,6 +47,34 @@
|
||||||
top: 2px;
|
top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#rates {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
// Remove focus style
|
||||||
|
.btn:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activated-btn {
|
||||||
|
color: #333;
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
border-color: #8c8c8c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-interactive-btn {
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&:hover, &:focus, &:active {
|
||||||
|
color: #333;
|
||||||
|
background-color: #fff;
|
||||||
|
border-color: #ccc;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#share, #more {
|
#share, #more {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { AuthService } from '../../core';
|
||||||
import { VideoMagnetComponent } from './video-magnet.component';
|
import { VideoMagnetComponent } from './video-magnet.component';
|
||||||
import { VideoShareComponent } from './video-share.component';
|
import { VideoShareComponent } from './video-share.component';
|
||||||
import { VideoReportComponent } from './video-report.component';
|
import { VideoReportComponent } from './video-report.component';
|
||||||
import { Video, VideoService } from '../shared';
|
import { RateType, Video, VideoService } from '../shared';
|
||||||
import { WebTorrentService } from './webtorrent.service';
|
import { WebTorrentService } from './webtorrent.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -33,6 +33,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
player: VideoJSPlayer;
|
player: VideoJSPlayer;
|
||||||
playerElement: Element;
|
playerElement: Element;
|
||||||
uploadSpeed: number;
|
uploadSpeed: number;
|
||||||
|
userRating: RateType = null;
|
||||||
video: Video = null;
|
video: Video = null;
|
||||||
videoNotFound = false;
|
videoNotFound = false;
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
this.video = video;
|
this.video = video;
|
||||||
this.setOpenGraphTags();
|
this.setOpenGraphTags();
|
||||||
this.loadVideo();
|
this.loadVideo();
|
||||||
|
this.checkUserRating();
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
this.videoNotFound = true;
|
this.videoNotFound = true;
|
||||||
|
@ -136,6 +138,40 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLike() {
|
||||||
|
if (this.isUserLoggedIn() === false) return;
|
||||||
|
// Already liked this video
|
||||||
|
if (this.userRating === 'like') return;
|
||||||
|
|
||||||
|
this.videoService.setVideoLike(this.video.id)
|
||||||
|
.subscribe(
|
||||||
|
() => {
|
||||||
|
// Update the video like attribute
|
||||||
|
this.updateVideoRating(this.userRating, 'like');
|
||||||
|
this.userRating = 'like';
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.notificationsService.error('Error', err.text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setDislike() {
|
||||||
|
if (this.isUserLoggedIn() === false) return;
|
||||||
|
// Already disliked this video
|
||||||
|
if (this.userRating === 'dislike') return;
|
||||||
|
|
||||||
|
this.videoService.setVideoDislike(this.video.id)
|
||||||
|
.subscribe(
|
||||||
|
() => {
|
||||||
|
// Update the video dislike attribute
|
||||||
|
this.updateVideoRating(this.userRating, 'dislike');
|
||||||
|
this.userRating = 'dislike';
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.notificationsService.error('Error', err.text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
showReportModal(event: Event) {
|
showReportModal(event: Event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.videoReportModal.show();
|
this.videoReportModal.show();
|
||||||
|
@ -154,6 +190,38 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
return this.authService.isLoggedIn();
|
return this.authService.isLoggedIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkUserRating() {
|
||||||
|
// Unlogged users do not have ratings
|
||||||
|
if (this.isUserLoggedIn() === false) return;
|
||||||
|
|
||||||
|
this.videoService.getUserVideoRating(this.video.id)
|
||||||
|
.subscribe(
|
||||||
|
ratingObject => {
|
||||||
|
if (ratingObject) {
|
||||||
|
this.userRating = ratingObject.rating;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
err => this.notificationsService.error('Error', err.text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateVideoRating(oldRating: RateType, newRating: RateType) {
|
||||||
|
let likesToIncrement = 0;
|
||||||
|
let dislikesToIncrement = 0;
|
||||||
|
|
||||||
|
if (oldRating) {
|
||||||
|
if (oldRating === 'like') likesToIncrement--;
|
||||||
|
if (oldRating === 'dislike') dislikesToIncrement--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newRating === 'like') likesToIncrement++;
|
||||||
|
if (newRating === 'dislike') dislikesToIncrement++;
|
||||||
|
|
||||||
|
this.video.likes += likesToIncrement;
|
||||||
|
this.video.dislikes += dislikesToIncrement;
|
||||||
|
}
|
||||||
|
|
||||||
private loadTooLong() {
|
private loadTooLong() {
|
||||||
this.error = true;
|
this.error = true;
|
||||||
console.error('The video load seems to be abnormally long.');
|
console.error('The video load seems to be abnormally long.');
|
||||||
|
|
|
@ -11,6 +11,7 @@ const secureMiddleware = middlewares.secure
|
||||||
const videosValidators = middlewares.validators.remote.videos
|
const videosValidators = middlewares.validators.remote.videos
|
||||||
const signatureValidators = middlewares.validators.remote.signature
|
const signatureValidators = middlewares.validators.remote.signature
|
||||||
const logger = require('../../../helpers/logger')
|
const logger = require('../../../helpers/logger')
|
||||||
|
const friends = require('../../../lib/friends')
|
||||||
const databaseUtils = require('../../../helpers/database-utils')
|
const databaseUtils = require('../../../helpers/database-utils')
|
||||||
|
|
||||||
const ENDPOINT_ACTIONS = constants.REQUEST_ENDPOINT_ACTIONS[constants.REQUEST_ENDPOINTS.VIDEOS]
|
const ENDPOINT_ACTIONS = constants.REQUEST_ENDPOINT_ACTIONS[constants.REQUEST_ENDPOINTS.VIDEOS]
|
||||||
|
@ -129,18 +130,22 @@ function processVideosEvents (eventData, fromPod, finalCallback) {
|
||||||
const options = { transaction: t }
|
const options = { transaction: t }
|
||||||
|
|
||||||
let columnToUpdate
|
let columnToUpdate
|
||||||
|
let qaduType
|
||||||
|
|
||||||
switch (eventData.eventType) {
|
switch (eventData.eventType) {
|
||||||
case constants.REQUEST_VIDEO_EVENT_TYPES.VIEWS:
|
case constants.REQUEST_VIDEO_EVENT_TYPES.VIEWS:
|
||||||
columnToUpdate = 'views'
|
columnToUpdate = 'views'
|
||||||
|
qaduType = constants.REQUEST_VIDEO_QADU_TYPES.VIEWS
|
||||||
break
|
break
|
||||||
|
|
||||||
case constants.REQUEST_VIDEO_EVENT_TYPES.LIKES:
|
case constants.REQUEST_VIDEO_EVENT_TYPES.LIKES:
|
||||||
columnToUpdate = 'likes'
|
columnToUpdate = 'likes'
|
||||||
|
qaduType = constants.REQUEST_VIDEO_QADU_TYPES.LIKES
|
||||||
break
|
break
|
||||||
|
|
||||||
case constants.REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
|
case constants.REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
|
||||||
columnToUpdate = 'dislikes'
|
columnToUpdate = 'dislikes'
|
||||||
|
qaduType = constants.REQUEST_VIDEO_QADU_TYPES.DISLIKES
|
||||||
break
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -151,6 +156,19 @@ function processVideosEvents (eventData, fromPod, finalCallback) {
|
||||||
query[columnToUpdate] = eventData.count
|
query[columnToUpdate] = eventData.count
|
||||||
|
|
||||||
videoInstance.increment(query, options).asCallback(function (err) {
|
videoInstance.increment(query, options).asCallback(function (err) {
|
||||||
|
return callback(err, t, videoInstance, qaduType)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
function sendQaduToFriends (t, videoInstance, qaduType, callback) {
|
||||||
|
const qadusParams = [
|
||||||
|
{
|
||||||
|
videoId: videoInstance.id,
|
||||||
|
type: qaduType
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
friends.quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
|
||||||
return callback(err, t)
|
return callback(err, t)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -159,7 +177,6 @@ function processVideosEvents (eventData, fromPod, finalCallback) {
|
||||||
|
|
||||||
], function (err, t) {
|
], function (err, t) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err)
|
|
||||||
logger.debug('Cannot process a video event.', { error: err })
|
logger.debug('Cannot process a video event.', { error: err })
|
||||||
return databaseUtils.rollbackTransaction(err, t, finalCallback)
|
return databaseUtils.rollbackTransaction(err, t, finalCallback)
|
||||||
}
|
}
|
||||||
|
@ -278,7 +295,10 @@ function addRemoteVideo (videoToCreateData, fromPod, finalCallback) {
|
||||||
duration: videoToCreateData.duration,
|
duration: videoToCreateData.duration,
|
||||||
createdAt: videoToCreateData.createdAt,
|
createdAt: videoToCreateData.createdAt,
|
||||||
// FIXME: updatedAt does not seems to be considered by Sequelize
|
// FIXME: updatedAt does not seems to be considered by Sequelize
|
||||||
updatedAt: videoToCreateData.updatedAt
|
updatedAt: videoToCreateData.updatedAt,
|
||||||
|
views: videoToCreateData.views,
|
||||||
|
likes: videoToCreateData.likes,
|
||||||
|
dislikes: videoToCreateData.dislikes
|
||||||
}
|
}
|
||||||
|
|
||||||
const video = db.Video.build(videoData)
|
const video = db.Video.build(videoData)
|
||||||
|
@ -372,6 +392,9 @@ function updateRemoteVideo (videoAttributesToUpdate, fromPod, finalCallback) {
|
||||||
videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
|
videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
|
||||||
videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
|
videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
|
||||||
videoInstance.set('extname', videoAttributesToUpdate.extname)
|
videoInstance.set('extname', videoAttributesToUpdate.extname)
|
||||||
|
videoInstance.set('views', videoAttributesToUpdate.views)
|
||||||
|
videoInstance.set('likes', videoAttributesToUpdate.likes)
|
||||||
|
videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
|
||||||
|
|
||||||
videoInstance.save(options).asCallback(function (err) {
|
videoInstance.save(options).asCallback(function (err) {
|
||||||
return callback(err, t, videoInstance, tagInstances)
|
return callback(err, t, videoInstance, tagInstances)
|
||||||
|
|
|
@ -18,7 +18,16 @@ const validatorsUsers = middlewares.validators.users
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
|
||||||
router.get('/me', oAuth.authenticate, getUserInformation)
|
router.get('/me',
|
||||||
|
oAuth.authenticate,
|
||||||
|
getUserInformation
|
||||||
|
)
|
||||||
|
|
||||||
|
router.get('/me/videos/:videoId/rating',
|
||||||
|
oAuth.authenticate,
|
||||||
|
validatorsUsers.usersVideoRating,
|
||||||
|
getUserVideoRating
|
||||||
|
)
|
||||||
|
|
||||||
router.get('/',
|
router.get('/',
|
||||||
validatorsPagination.pagination,
|
validatorsPagination.pagination,
|
||||||
|
@ -80,6 +89,22 @@ function getUserInformation (req, res, next) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUserVideoRating (req, res, next) {
|
||||||
|
const videoId = req.params.videoId
|
||||||
|
const userId = res.locals.oauth.token.User.id
|
||||||
|
|
||||||
|
db.UserVideoRate.load(userId, videoId, function (err, ratingObj) {
|
||||||
|
if (err) return next(err)
|
||||||
|
|
||||||
|
const rating = ratingObj ? ratingObj.type : 'none'
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
videoId,
|
||||||
|
rating
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function listUsers (req, res, next) {
|
function listUsers (req, res, next) {
|
||||||
db.User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) {
|
db.User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) {
|
||||||
if (err) return next(err)
|
if (err) return next(err)
|
||||||
|
|
|
@ -60,6 +60,12 @@ router.post('/:id/abuse',
|
||||||
reportVideoAbuseRetryWrapper
|
reportVideoAbuseRetryWrapper
|
||||||
)
|
)
|
||||||
|
|
||||||
|
router.put('/:id/rate',
|
||||||
|
oAuth.authenticate,
|
||||||
|
validatorsVideos.videoRate,
|
||||||
|
rateVideoRetryWrapper
|
||||||
|
)
|
||||||
|
|
||||||
router.get('/',
|
router.get('/',
|
||||||
validatorsPagination.pagination,
|
validatorsPagination.pagination,
|
||||||
validatorsSort.videosSort,
|
validatorsSort.videosSort,
|
||||||
|
@ -104,6 +110,147 @@ module.exports = router
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function rateVideoRetryWrapper (req, res, next) {
|
||||||
|
const options = {
|
||||||
|
arguments: [ req, res ],
|
||||||
|
errorMessage: 'Cannot update the user video rate.'
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseUtils.retryTransactionWrapper(rateVideo, options, function (err) {
|
||||||
|
if (err) return next(err)
|
||||||
|
|
||||||
|
return res.type('json').status(204).end()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function rateVideo (req, res, finalCallback) {
|
||||||
|
const rateType = req.body.rating
|
||||||
|
const videoInstance = res.locals.video
|
||||||
|
const userInstance = res.locals.oauth.token.User
|
||||||
|
|
||||||
|
waterfall([
|
||||||
|
databaseUtils.startSerializableTransaction,
|
||||||
|
|
||||||
|
function findPreviousRate (t, callback) {
|
||||||
|
db.UserVideoRate.load(userInstance.id, videoInstance.id, t, function (err, previousRate) {
|
||||||
|
return callback(err, t, previousRate)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
function insertUserRateIntoDB (t, previousRate, callback) {
|
||||||
|
const options = { transaction: t }
|
||||||
|
|
||||||
|
let likesToIncrement = 0
|
||||||
|
let dislikesToIncrement = 0
|
||||||
|
|
||||||
|
if (rateType === constants.VIDEO_RATE_TYPES.LIKE) likesToIncrement++
|
||||||
|
else if (rateType === constants.VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement++
|
||||||
|
|
||||||
|
// There was a previous rate, update it
|
||||||
|
if (previousRate) {
|
||||||
|
// We will remove the previous rate, so we will need to remove it from the video attribute
|
||||||
|
if (previousRate.type === constants.VIDEO_RATE_TYPES.LIKE) likesToIncrement--
|
||||||
|
else if (previousRate.type === constants.VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
|
||||||
|
|
||||||
|
previousRate.type = rateType
|
||||||
|
|
||||||
|
previousRate.save(options).asCallback(function (err) {
|
||||||
|
return callback(err, t, likesToIncrement, dislikesToIncrement)
|
||||||
|
})
|
||||||
|
} else { // There was not a previous rate, insert a new one
|
||||||
|
const query = {
|
||||||
|
userId: userInstance.id,
|
||||||
|
videoId: videoInstance.id,
|
||||||
|
type: rateType
|
||||||
|
}
|
||||||
|
|
||||||
|
db.UserVideoRate.create(query, options).asCallback(function (err) {
|
||||||
|
return callback(err, t, likesToIncrement, dislikesToIncrement)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
function updateVideoAttributeDB (t, likesToIncrement, dislikesToIncrement, callback) {
|
||||||
|
const options = { transaction: t }
|
||||||
|
const incrementQuery = {
|
||||||
|
likes: likesToIncrement,
|
||||||
|
dislikes: dislikesToIncrement
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if we do not own the video we increment the attributes
|
||||||
|
// It is usefull for the user to have a feedback
|
||||||
|
videoInstance.increment(incrementQuery, options).asCallback(function (err) {
|
||||||
|
return callback(err, t, likesToIncrement, dislikesToIncrement)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
function sendEventsToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
|
||||||
|
// No need for an event type, we own the video
|
||||||
|
if (videoInstance.isOwned()) return callback(null, t, likesToIncrement, dislikesToIncrement)
|
||||||
|
|
||||||
|
const eventsParams = []
|
||||||
|
|
||||||
|
if (likesToIncrement !== 0) {
|
||||||
|
eventsParams.push({
|
||||||
|
videoId: videoInstance.id,
|
||||||
|
type: constants.REQUEST_VIDEO_EVENT_TYPES.LIKES,
|
||||||
|
count: likesToIncrement
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dislikesToIncrement !== 0) {
|
||||||
|
eventsParams.push({
|
||||||
|
videoId: videoInstance.id,
|
||||||
|
type: constants.REQUEST_VIDEO_EVENT_TYPES.DISLIKES,
|
||||||
|
count: dislikesToIncrement
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
friends.addEventsToRemoteVideo(eventsParams, t, function (err) {
|
||||||
|
return callback(err, t, likesToIncrement, dislikesToIncrement)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
function sendQaduToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
|
||||||
|
// We do not own the video, there is no need to send a quick and dirty update to friends
|
||||||
|
// Our rate was already sent by the addEvent function
|
||||||
|
if (videoInstance.isOwned() === false) return callback(null, t)
|
||||||
|
|
||||||
|
const qadusParams = []
|
||||||
|
|
||||||
|
if (likesToIncrement !== 0) {
|
||||||
|
qadusParams.push({
|
||||||
|
videoId: videoInstance.id,
|
||||||
|
type: constants.REQUEST_VIDEO_QADU_TYPES.LIKES
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dislikesToIncrement !== 0) {
|
||||||
|
qadusParams.push({
|
||||||
|
videoId: videoInstance.id,
|
||||||
|
type: constants.REQUEST_VIDEO_QADU_TYPES.DISLIKES
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
friends.quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
|
||||||
|
return callback(err, t)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
databaseUtils.commitTransaction
|
||||||
|
|
||||||
|
], function (err, t) {
|
||||||
|
if (err) {
|
||||||
|
// This is just a debug because we will retry the insert
|
||||||
|
logger.debug('Cannot add the user video rate.', { error: err })
|
||||||
|
return databaseUtils.rollbackTransaction(err, t, finalCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username)
|
||||||
|
return finalCallback(null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Wrapper to video add that retry the function if there is a database error
|
// Wrapper to video add that retry the function if there is a database error
|
||||||
// We need this because we run the transaction in SERIALIZABLE isolation that can fail
|
// We need this because we run the transaction in SERIALIZABLE isolation that can fail
|
||||||
function addVideoRetryWrapper (req, res, next) {
|
function addVideoRetryWrapper (req, res, next) {
|
||||||
|
@ -155,8 +302,7 @@ function addVideo (req, res, videoFile, finalCallback) {
|
||||||
extname: path.extname(videoFile.filename),
|
extname: path.extname(videoFile.filename),
|
||||||
description: videoInfos.description,
|
description: videoInfos.description,
|
||||||
duration: videoFile.duration,
|
duration: videoFile.duration,
|
||||||
authorId: author.id,
|
authorId: author.id
|
||||||
views: videoInfos.views
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const video = db.Video.build(videoData)
|
const video = db.Video.build(videoData)
|
||||||
|
@ -332,11 +478,19 @@ function getVideo (req, res, next) {
|
||||||
|
|
||||||
// FIXME: make a real view system
|
// FIXME: make a real view system
|
||||||
// For example, only add a view when a user watch a video during 30s etc
|
// For example, only add a view when a user watch a video during 30s etc
|
||||||
friends.quickAndDirtyUpdateVideoToFriends(videoInstance.id, constants.REQUEST_VIDEO_QADU_TYPES.VIEWS)
|
const qaduParams = {
|
||||||
|
videoId: videoInstance.id,
|
||||||
|
type: constants.REQUEST_VIDEO_QADU_TYPES.VIEWS
|
||||||
|
}
|
||||||
|
friends.quickAndDirtyUpdateVideoToFriends(qaduParams)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// Just send the event to our friends
|
// Just send the event to our friends
|
||||||
friends.addEventToRemoteVideo(videoInstance.id, constants.REQUEST_VIDEO_EVENT_TYPES.VIEWS)
|
const eventParams = {
|
||||||
|
videoId: videoInstance.id,
|
||||||
|
type: constants.REQUEST_VIDEO_EVENT_TYPES.VIEWS
|
||||||
|
}
|
||||||
|
friends.addEventToRemoteVideo(eventParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not wait the view system
|
// Do not wait the view system
|
||||||
|
|
|
@ -92,7 +92,9 @@ function isCommonVideoAttributesValid (video) {
|
||||||
videosValidators.isVideoTagsValid(video.tags) &&
|
videosValidators.isVideoTagsValid(video.tags) &&
|
||||||
videosValidators.isVideoRemoteIdValid(video.remoteId) &&
|
videosValidators.isVideoRemoteIdValid(video.remoteId) &&
|
||||||
videosValidators.isVideoExtnameValid(video.extname) &&
|
videosValidators.isVideoExtnameValid(video.extname) &&
|
||||||
videosValidators.isVideoViewsValid(video.views)
|
videosValidators.isVideoViewsValid(video.views) &&
|
||||||
|
videosValidators.isVideoLikesValid(video.likes) &&
|
||||||
|
videosValidators.isVideoDislikesValid(video.dislikes)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRequestTypeAddValid (value) {
|
function isRequestTypeAddValid (value) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const validator = require('express-validator').validator
|
const validator = require('express-validator').validator
|
||||||
|
const values = require('lodash/values')
|
||||||
|
|
||||||
const constants = require('../../initializers/constants')
|
const constants = require('../../initializers/constants')
|
||||||
const usersValidators = require('./users')
|
const usersValidators = require('./users')
|
||||||
|
@ -26,6 +27,7 @@ const videosValidators = {
|
||||||
isVideoFile,
|
isVideoFile,
|
||||||
isVideoViewsValid,
|
isVideoViewsValid,
|
||||||
isVideoLikesValid,
|
isVideoLikesValid,
|
||||||
|
isVideoRatingTypeValid,
|
||||||
isVideoDislikesValid,
|
isVideoDislikesValid,
|
||||||
isVideoEventCountValid
|
isVideoEventCountValid
|
||||||
}
|
}
|
||||||
|
@ -103,6 +105,10 @@ function isVideoEventCountValid (value) {
|
||||||
return validator.isInt(value + '', VIDEO_EVENTS_CONSTRAINTS_FIELDS.COUNT)
|
return validator.isInt(value + '', VIDEO_EVENTS_CONSTRAINTS_FIELDS.COUNT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isVideoRatingTypeValid (value) {
|
||||||
|
return values(constants.VIDEO_RATE_TYPES).indexOf(value) !== -1
|
||||||
|
}
|
||||||
|
|
||||||
function isVideoFile (value, files) {
|
function isVideoFile (value, files) {
|
||||||
// Should have files
|
// Should have files
|
||||||
if (!files) return false
|
if (!files) return false
|
||||||
|
|
|
@ -5,7 +5,7 @@ const path = require('path')
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const LAST_MIGRATION_VERSION = 15
|
const LAST_MIGRATION_VERSION = 25
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -95,6 +95,11 @@ const CONSTRAINTS_FIELDS = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VIDEO_RATE_TYPES = {
|
||||||
|
LIKE: 'like',
|
||||||
|
DISLIKE: 'dislike'
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// Score a pod has when we create it as a friend
|
// Score a pod has when we create it as a friend
|
||||||
|
@ -249,7 +254,8 @@ module.exports = {
|
||||||
STATIC_MAX_AGE,
|
STATIC_MAX_AGE,
|
||||||
STATIC_PATHS,
|
STATIC_PATHS,
|
||||||
THUMBNAILS_SIZE,
|
THUMBNAILS_SIZE,
|
||||||
USER_ROLES
|
USER_ROLES,
|
||||||
|
VIDEO_RATE_TYPES
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
||||||
|
exports.up = function (utils, finalCallback) {
|
||||||
|
const q = utils.queryInterface
|
||||||
|
const Sequelize = utils.Sequelize
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
q.addColumn('Videos', 'likes', data, { transaction: utils.transaction }).asCallback(finalCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = function (options, callback) {
|
||||||
|
throw new Error('Not implemented.')
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
// utils = { transaction, queryInterface, sequelize, Sequelize }
|
||||||
|
exports.up = function (utils, finalCallback) {
|
||||||
|
const q = utils.queryInterface
|
||||||
|
const Sequelize = utils.Sequelize
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
q.addColumn('Videos', 'dislikes', data, { transaction: utils.transaction }).asCallback(finalCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.down = function (options, callback) {
|
||||||
|
throw new Error('Not implemented.')
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
const each = require('async/each')
|
const each = require('async/each')
|
||||||
const eachLimit = require('async/eachLimit')
|
const eachLimit = require('async/eachLimit')
|
||||||
const eachSeries = require('async/eachSeries')
|
const eachSeries = require('async/eachSeries')
|
||||||
|
const series = require('async/series')
|
||||||
const request = require('request')
|
const request = require('request')
|
||||||
const waterfall = require('async/waterfall')
|
const waterfall = require('async/waterfall')
|
||||||
|
|
||||||
|
@ -28,7 +29,9 @@ const friends = {
|
||||||
updateVideoToFriends,
|
updateVideoToFriends,
|
||||||
reportAbuseVideoToFriend,
|
reportAbuseVideoToFriend,
|
||||||
quickAndDirtyUpdateVideoToFriends,
|
quickAndDirtyUpdateVideoToFriends,
|
||||||
|
quickAndDirtyUpdatesVideoToFriends,
|
||||||
addEventToRemoteVideo,
|
addEventToRemoteVideo,
|
||||||
|
addEventsToRemoteVideo,
|
||||||
hasFriends,
|
hasFriends,
|
||||||
makeFriends,
|
makeFriends,
|
||||||
quitFriends,
|
quitFriends,
|
||||||
|
@ -84,24 +87,52 @@ function reportAbuseVideoToFriend (reportData, video) {
|
||||||
createRequest(options)
|
createRequest(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function quickAndDirtyUpdateVideoToFriends (videoId, type, transaction, callback) {
|
function quickAndDirtyUpdateVideoToFriends (qaduParams, transaction, callback) {
|
||||||
const options = {
|
const options = {
|
||||||
videoId,
|
videoId: qaduParams.videoId,
|
||||||
type,
|
type: qaduParams.type,
|
||||||
transaction
|
transaction
|
||||||
}
|
}
|
||||||
return createVideoQaduRequest(options, callback)
|
return createVideoQaduRequest(options, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEventToRemoteVideo (videoId, type, transaction, callback) {
|
function quickAndDirtyUpdatesVideoToFriends (qadusParams, transaction, finalCallback) {
|
||||||
|
const tasks = []
|
||||||
|
|
||||||
|
qadusParams.forEach(function (qaduParams) {
|
||||||
|
const fun = function (callback) {
|
||||||
|
quickAndDirtyUpdateVideoToFriends(qaduParams, transaction, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.push(fun)
|
||||||
|
})
|
||||||
|
|
||||||
|
series(tasks, finalCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function addEventToRemoteVideo (eventParams, transaction, callback) {
|
||||||
const options = {
|
const options = {
|
||||||
videoId,
|
videoId: eventParams.videoId,
|
||||||
type,
|
type: eventParams.type,
|
||||||
transaction
|
transaction
|
||||||
}
|
}
|
||||||
createVideoEventRequest(options, callback)
|
createVideoEventRequest(options, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addEventsToRemoteVideo (eventsParams, transaction, finalCallback) {
|
||||||
|
const tasks = []
|
||||||
|
|
||||||
|
eventsParams.forEach(function (eventParams) {
|
||||||
|
const fun = function (callback) {
|
||||||
|
addEventToRemoteVideo(eventParams, transaction, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.push(fun)
|
||||||
|
})
|
||||||
|
|
||||||
|
series(tasks, finalCallback)
|
||||||
|
}
|
||||||
|
|
||||||
function hasFriends (callback) {
|
function hasFriends (callback) {
|
||||||
db.Pod.countAll(function (err, count) {
|
db.Pod.countAll(function (err, count) {
|
||||||
if (err) return callback(err)
|
if (err) return callback(err)
|
||||||
|
|
|
@ -44,14 +44,17 @@ module.exports = class RequestVideoQaduScheduler extends BaseRequestScheduler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const videoData = {}
|
// Maybe another attribute was filled for this video
|
||||||
|
let videoData = requestsToMakeGrouped[hashKey].videos[video.id]
|
||||||
|
if (!videoData) videoData = {}
|
||||||
|
|
||||||
switch (request.type) {
|
switch (request.type) {
|
||||||
case constants.REQUEST_VIDEO_QADU_TYPES.LIKES:
|
case constants.REQUEST_VIDEO_QADU_TYPES.LIKES:
|
||||||
videoData.likes = video.likes
|
videoData.likes = video.likes
|
||||||
break
|
break
|
||||||
|
|
||||||
case constants.REQUEST_VIDEO_QADU_TYPES.DISLIKES:
|
case constants.REQUEST_VIDEO_QADU_TYPES.DISLIKES:
|
||||||
videoData.likes = video.dislikes
|
videoData.dislikes = video.dislikes
|
||||||
break
|
break
|
||||||
|
|
||||||
case constants.REQUEST_VIDEO_QADU_TYPES.VIEWS:
|
case constants.REQUEST_VIDEO_QADU_TYPES.VIEWS:
|
||||||
|
|
|
@ -7,7 +7,8 @@ const logger = require('../../helpers/logger')
|
||||||
const validatorsUsers = {
|
const validatorsUsers = {
|
||||||
usersAdd,
|
usersAdd,
|
||||||
usersRemove,
|
usersRemove,
|
||||||
usersUpdate
|
usersUpdate,
|
||||||
|
usersVideoRating
|
||||||
}
|
}
|
||||||
|
|
||||||
function usersAdd (req, res, next) {
|
function usersAdd (req, res, next) {
|
||||||
|
@ -62,6 +63,25 @@ function usersUpdate (req, res, next) {
|
||||||
checkErrors(req, res, next)
|
checkErrors(req, res, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function usersVideoRating (req, res, next) {
|
||||||
|
req.checkParams('videoId', 'Should have a valid video id').notEmpty().isUUID(4)
|
||||||
|
|
||||||
|
logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
|
||||||
|
|
||||||
|
checkErrors(req, res, function () {
|
||||||
|
db.Video.load(req.params.videoId, function (err, video) {
|
||||||
|
if (err) {
|
||||||
|
logger.error('Error in user request validator.', { error: err })
|
||||||
|
return res.sendStatus(500)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!video) return res.status(404).send('Video not found')
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
module.exports = validatorsUsers
|
module.exports = validatorsUsers
|
||||||
|
|
|
@ -13,7 +13,9 @@ const validatorsVideos = {
|
||||||
videosRemove,
|
videosRemove,
|
||||||
videosSearch,
|
videosSearch,
|
||||||
|
|
||||||
videoAbuseReport
|
videoAbuseReport,
|
||||||
|
|
||||||
|
videoRate
|
||||||
}
|
}
|
||||||
|
|
||||||
function videosAdd (req, res, next) {
|
function videosAdd (req, res, next) {
|
||||||
|
@ -119,6 +121,17 @@ function videoAbuseReport (req, res, next) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function videoRate (req, res, next) {
|
||||||
|
req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
|
||||||
|
req.checkBody('rating', 'Should have a valid rate type').isVideoRatingTypeValid()
|
||||||
|
|
||||||
|
logger.debug('Checking videoRate parameters', { parameters: req.body })
|
||||||
|
|
||||||
|
checkErrors(req, res, function () {
|
||||||
|
checkVideoExists(req.params.id, res, next)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
module.exports = validatorsVideos
|
module.exports = validatorsVideos
|
||||||
|
|
|
@ -83,6 +83,9 @@ function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
|
||||||
if (podIds.length === 0) return callback(null, [])
|
if (podIds.length === 0) return callback(null, [])
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
|
order: [
|
||||||
|
[ 'id', 'ASC' ]
|
||||||
|
],
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: self.sequelize.models.Video,
|
model: self.sequelize.models.Video,
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/*
|
||||||
|
User rates per video.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const values = require('lodash/values')
|
||||||
|
|
||||||
|
const constants = require('../initializers/constants')
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
module.exports = function (sequelize, DataTypes) {
|
||||||
|
const UserVideoRate = sequelize.define('UserVideoRate',
|
||||||
|
{
|
||||||
|
type: {
|
||||||
|
type: DataTypes.ENUM(values(constants.VIDEO_RATE_TYPES)),
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
fields: [ 'videoId', 'userId', 'type' ],
|
||||||
|
unique: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
classMethods: {
|
||||||
|
associate,
|
||||||
|
|
||||||
|
load
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return UserVideoRate
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------ STATICS ------------------------------
|
||||||
|
|
||||||
|
function associate (models) {
|
||||||
|
this.belongsTo(models.Video, {
|
||||||
|
foreignKey: {
|
||||||
|
name: 'videoId',
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
|
||||||
|
this.belongsTo(models.User, {
|
||||||
|
foreignKey: {
|
||||||
|
name: 'userId',
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function load (userId, videoId, transaction, callback) {
|
||||||
|
if (!callback) {
|
||||||
|
callback = transaction
|
||||||
|
transaction = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = {
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
videoId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {}
|
||||||
|
if (transaction) options.transaction = transaction
|
||||||
|
|
||||||
|
return this.findOne(query, options).asCallback(callback)
|
||||||
|
}
|
|
@ -89,6 +89,24 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
min: 0,
|
min: 0,
|
||||||
isInt: true
|
isInt: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
likes: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
validate: {
|
||||||
|
min: 0,
|
||||||
|
isInt: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dislikes: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0,
|
||||||
|
validate: {
|
||||||
|
min: 0,
|
||||||
|
isInt: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -113,6 +131,9 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: [ 'views' ]
|
fields: [ 'views' ]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fields: [ 'likes' ]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
classMethods: {
|
classMethods: {
|
||||||
|
@ -349,6 +370,8 @@ function toFormatedJSON () {
|
||||||
author: this.Author.name,
|
author: this.Author.name,
|
||||||
duration: this.duration,
|
duration: this.duration,
|
||||||
views: this.views,
|
views: this.views,
|
||||||
|
likes: this.likes,
|
||||||
|
dislikes: this.dislikes,
|
||||||
tags: map(this.Tags, 'name'),
|
tags: map(this.Tags, 'name'),
|
||||||
thumbnailPath: pathUtils.join(constants.STATIC_PATHS.THUMBNAILS, this.getThumbnailName()),
|
thumbnailPath: pathUtils.join(constants.STATIC_PATHS.THUMBNAILS, this.getThumbnailName()),
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
|
@ -381,7 +404,9 @@ function toAddRemoteJSON (callback) {
|
||||||
createdAt: self.createdAt,
|
createdAt: self.createdAt,
|
||||||
updatedAt: self.updatedAt,
|
updatedAt: self.updatedAt,
|
||||||
extname: self.extname,
|
extname: self.extname,
|
||||||
views: self.views
|
views: self.views,
|
||||||
|
likes: self.likes,
|
||||||
|
dislikes: self.dislikes
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback(null, remoteVideo)
|
return callback(null, remoteVideo)
|
||||||
|
@ -400,7 +425,9 @@ function toUpdateRemoteJSON (callback) {
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
updatedAt: this.updatedAt,
|
updatedAt: this.updatedAt,
|
||||||
extname: this.extname,
|
extname: this.extname,
|
||||||
views: this.views
|
views: this.views,
|
||||||
|
likes: this.likes,
|
||||||
|
dislikes: this.dislikes
|
||||||
}
|
}
|
||||||
|
|
||||||
return json
|
return json
|
||||||
|
|
|
@ -9,11 +9,13 @@ const loginUtils = require('../../utils/login')
|
||||||
const requestsUtils = require('../../utils/requests')
|
const requestsUtils = require('../../utils/requests')
|
||||||
const serversUtils = require('../../utils/servers')
|
const serversUtils = require('../../utils/servers')
|
||||||
const usersUtils = require('../../utils/users')
|
const usersUtils = require('../../utils/users')
|
||||||
|
const videosUtils = require('../../utils/videos')
|
||||||
|
|
||||||
describe('Test users API validators', function () {
|
describe('Test users API validators', function () {
|
||||||
const path = '/api/v1/users/'
|
const path = '/api/v1/users/'
|
||||||
let userId = null
|
let userId = null
|
||||||
let rootId = null
|
let rootId = null
|
||||||
|
let videoId = null
|
||||||
let server = null
|
let server = null
|
||||||
let userAccessToken = null
|
let userAccessToken = null
|
||||||
|
|
||||||
|
@ -47,6 +49,23 @@ describe('Test users API validators', function () {
|
||||||
|
|
||||||
usersUtils.createUser(server.url, server.accessToken, username, password, next)
|
usersUtils.createUser(server.url, server.accessToken, username, password, next)
|
||||||
},
|
},
|
||||||
|
function (next) {
|
||||||
|
const name = 'my super name for pod'
|
||||||
|
const description = 'my super description for pod'
|
||||||
|
const tags = [ 'tag' ]
|
||||||
|
const file = 'video_short2.webm'
|
||||||
|
videosUtils.uploadVideo(server.url, server.accessToken, name, description, tags, file, next)
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
videosUtils.getVideosList(server.url, function (err, res) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
videoId = videos[0].id
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
},
|
||||||
function (next) {
|
function (next) {
|
||||||
const user = {
|
const user = {
|
||||||
username: 'user1',
|
username: 'user1',
|
||||||
|
@ -289,6 +308,63 @@ describe('Test users API validators', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('When getting my video rating', function () {
|
||||||
|
it('Should fail with a non authenticated user', function (done) {
|
||||||
|
request(server.url)
|
||||||
|
.get(path + 'me/videos/' + videoId + '/rating')
|
||||||
|
.set('Authorization', 'Bearer faketoken')
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(401, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with an incorrect video uuid', function (done) {
|
||||||
|
request(server.url)
|
||||||
|
.get(path + 'me/videos/blabla/rating')
|
||||||
|
.set('Authorization', 'Bearer ' + userAccessToken)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(400, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with an unknown video', function (done) {
|
||||||
|
request(server.url)
|
||||||
|
.get(path + 'me/videos/4da6fde3-88f7-4d16-b119-108df5630b06/rating')
|
||||||
|
.set('Authorization', 'Bearer ' + userAccessToken)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(404, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should success with the correct parameters', function (done) {
|
||||||
|
request(server.url)
|
||||||
|
.get(path + 'me/videos/' + videoId + '/rating')
|
||||||
|
.set('Authorization', 'Bearer ' + userAccessToken)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.expect(200, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('When removing an user', function () {
|
||||||
|
it('Should fail with an incorrect id', function (done) {
|
||||||
|
request(server.url)
|
||||||
|
.delete(path + 'bla-bla')
|
||||||
|
.set('Authorization', 'Bearer ' + server.accessToken)
|
||||||
|
.expect(400, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with the root user', function (done) {
|
||||||
|
request(server.url)
|
||||||
|
.delete(path + rootId)
|
||||||
|
.set('Authorization', 'Bearer ' + server.accessToken)
|
||||||
|
.expect(400, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should return 404 with a non existing id', function (done) {
|
||||||
|
request(server.url)
|
||||||
|
.delete(path + '45')
|
||||||
|
.set('Authorization', 'Bearer ' + server.accessToken)
|
||||||
|
.expect(404, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('When removing an user', function () {
|
describe('When removing an user', function () {
|
||||||
it('Should fail with an incorrect id', function (done) {
|
it('Should fail with an incorrect id', function (done) {
|
||||||
request(server.url)
|
request(server.url)
|
||||||
|
|
|
@ -420,6 +420,48 @@ describe('Test videos API validator', function () {
|
||||||
it('Should succeed with the correct parameters')
|
it('Should succeed with the correct parameters')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('When rating a video', function () {
|
||||||
|
let videoId
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
videosUtils.getVideosList(server.url, function (err, res) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
videoId = res.body.data[0].id
|
||||||
|
|
||||||
|
return done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail without a valid uuid', function (done) {
|
||||||
|
const data = {
|
||||||
|
rating: 'like'
|
||||||
|
}
|
||||||
|
requestsUtils.makePutBodyRequest(server.url, path + 'blabla/rate', server.accessToken, data, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with an unknown id', function (done) {
|
||||||
|
const data = {
|
||||||
|
rating: 'like'
|
||||||
|
}
|
||||||
|
requestsUtils.makePutBodyRequest(server.url, path + '4da6fde3-88f7-4d16-b119-108df5630b06/rate', server.accessToken, data, done, 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a wrong rating', function (done) {
|
||||||
|
const data = {
|
||||||
|
rating: 'likes'
|
||||||
|
}
|
||||||
|
requestsUtils.makePutBodyRequest(server.url, path + videoId + '/rate', server.accessToken, data, done)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should succeed with the correct parameters', function (done) {
|
||||||
|
const data = {
|
||||||
|
rating: 'like'
|
||||||
|
}
|
||||||
|
requestsUtils.makePutBodyRequest(server.url, path + videoId + '/rate', server.accessToken, data, done, 204)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('When removing a video', function () {
|
describe('When removing a video', function () {
|
||||||
it('Should have 404 with nothing', function (done) {
|
it('Should have 404 with nothing', function (done) {
|
||||||
request(server.url)
|
request(server.url)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
const chai = require('chai')
|
const chai = require('chai')
|
||||||
const each = require('async/each')
|
const each = require('async/each')
|
||||||
|
const eachSeries = require('async/eachSeries')
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
const parallel = require('async/parallel')
|
const parallel = require('async/parallel')
|
||||||
const series = require('async/series')
|
const series = require('async/series')
|
||||||
|
@ -378,7 +379,7 @@ describe('Test multiple pods', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Should update video views', function () {
|
describe('Should update video views, likes and dislikes', function () {
|
||||||
let localVideosPod3 = []
|
let localVideosPod3 = []
|
||||||
let remoteVideosPod1 = []
|
let remoteVideosPod1 = []
|
||||||
let remoteVideosPod2 = []
|
let remoteVideosPod2 = []
|
||||||
|
@ -419,7 +420,7 @@ describe('Test multiple pods', function () {
|
||||||
], done)
|
], done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should views multiple videos on owned servers', function (done) {
|
it('Should view multiple videos on owned servers', function (done) {
|
||||||
this.timeout(30000)
|
this.timeout(30000)
|
||||||
|
|
||||||
parallel([
|
parallel([
|
||||||
|
@ -440,18 +441,18 @@ describe('Test multiple pods', function () {
|
||||||
},
|
},
|
||||||
|
|
||||||
function (callback) {
|
function (callback) {
|
||||||
setTimeout(done, 22000)
|
setTimeout(callback, 22000)
|
||||||
}
|
}
|
||||||
], function (err) {
|
], function (err) {
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
|
|
||||||
each(servers, function (server, callback) {
|
eachSeries(servers, function (server, callback) {
|
||||||
videosUtils.getVideosList(server.url, function (err, res) {
|
videosUtils.getVideosList(server.url, function (err, res) {
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
|
|
||||||
const videos = res.body.data
|
const videos = res.body.data
|
||||||
expect(videos.find(video => video.views === 3)).to.be.exist
|
expect(videos.find(video => video.views === 3)).to.exist
|
||||||
expect(videos.find(video => video.views === 1)).to.be.exist
|
expect(videos.find(video => video.views === 1)).to.exist
|
||||||
|
|
||||||
callback()
|
callback()
|
||||||
})
|
})
|
||||||
|
@ -459,7 +460,7 @@ describe('Test multiple pods', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should views multiple videos on each servers', function (done) {
|
it('Should view multiple videos on each servers', function (done) {
|
||||||
this.timeout(30000)
|
this.timeout(30000)
|
||||||
|
|
||||||
parallel([
|
parallel([
|
||||||
|
@ -504,17 +505,17 @@ describe('Test multiple pods', function () {
|
||||||
},
|
},
|
||||||
|
|
||||||
function (callback) {
|
function (callback) {
|
||||||
setTimeout(done, 22000)
|
setTimeout(callback, 22000)
|
||||||
}
|
}
|
||||||
], function (err) {
|
], function (err) {
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
|
|
||||||
let baseVideos = null
|
let baseVideos = null
|
||||||
each(servers, function (server, callback) {
|
eachSeries(servers, function (server, callback) {
|
||||||
videosUtils.getVideosList(server.url, function (err, res) {
|
videosUtils.getVideosList(server.url, function (err, res) {
|
||||||
if (err) throw err
|
if (err) throw err
|
||||||
|
|
||||||
const videos = res.body
|
const videos = res.body.data
|
||||||
|
|
||||||
// Initialize base videos for future comparisons
|
// Initialize base videos for future comparisons
|
||||||
if (baseVideos === null) {
|
if (baseVideos === null) {
|
||||||
|
@ -522,9 +523,73 @@ describe('Test multiple pods', function () {
|
||||||
return callback()
|
return callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < baseVideos.length; i++) {
|
baseVideos.forEach(baseVideo => {
|
||||||
expect(baseVideos[i].views).to.equal(videos[i].views)
|
const sameVideo = videos.find(video => video.name === baseVideo.name)
|
||||||
|
expect(baseVideo.views).to.equal(sameVideo.views)
|
||||||
|
})
|
||||||
|
|
||||||
|
callback()
|
||||||
|
})
|
||||||
|
}, done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should like and dislikes videos on different services', function (done) {
|
||||||
|
this.timeout(30000)
|
||||||
|
|
||||||
|
parallel([
|
||||||
|
function (callback) {
|
||||||
|
videosUtils.rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'like', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
function (callback) {
|
||||||
|
videosUtils.rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'dislike', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
function (callback) {
|
||||||
|
videosUtils.rateVideo(servers[0].url, servers[0].accessToken, remoteVideosPod1[0], 'like', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
function (callback) {
|
||||||
|
videosUtils.rateVideo(servers[2].url, servers[2].accessToken, localVideosPod3[1], 'like', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
function (callback) {
|
||||||
|
videosUtils.rateVideo(servers[2].url, servers[2].accessToken, localVideosPod3[1], 'dislike', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
function (callback) {
|
||||||
|
videosUtils.rateVideo(servers[2].url, servers[2].accessToken, remoteVideosPod3[1], 'dislike', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
function (callback) {
|
||||||
|
videosUtils.rateVideo(servers[2].url, servers[2].accessToken, remoteVideosPod3[0], 'like', callback)
|
||||||
|
},
|
||||||
|
|
||||||
|
function (callback) {
|
||||||
|
setTimeout(callback, 22000)
|
||||||
}
|
}
|
||||||
|
], function (err) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
let baseVideos = null
|
||||||
|
eachSeries(servers, function (server, callback) {
|
||||||
|
videosUtils.getVideosList(server.url, function (err, res) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
const videos = res.body.data
|
||||||
|
|
||||||
|
// Initialize base videos for future comparisons
|
||||||
|
if (baseVideos === null) {
|
||||||
|
baseVideos = videos
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
baseVideos.forEach(baseVideo => {
|
||||||
|
const sameVideo = videos.find(video => video.name === baseVideo.name)
|
||||||
|
expect(baseVideo.likes).to.equal(sameVideo.likes)
|
||||||
|
expect(baseVideo.dislikes).to.equal(sameVideo.dislikes)
|
||||||
|
})
|
||||||
|
|
||||||
callback()
|
callback()
|
||||||
})
|
})
|
||||||
|
|
|
@ -609,6 +609,40 @@ describe('Test a single pod', function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should like a video', function (done) {
|
||||||
|
videosUtils.rateVideo(server.url, server.accessToken, videoId, 'like', function (err) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
videosUtils.getVideo(server.url, videoId, function (err, res) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
const video = res.body
|
||||||
|
|
||||||
|
expect(video.likes).to.equal(1)
|
||||||
|
expect(video.dislikes).to.equal(0)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should dislike the same video', function (done) {
|
||||||
|
videosUtils.rateVideo(server.url, server.accessToken, videoId, 'dislike', function (err) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
videosUtils.getVideo(server.url, videoId, function (err, res) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
const video = res.body
|
||||||
|
|
||||||
|
expect(video.likes).to.equal(0)
|
||||||
|
expect(video.dislikes).to.equal(1)
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
after(function (done) {
|
after(function (done) {
|
||||||
process.kill(-server.app.pid)
|
process.kill(-server.app.pid)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ const loginUtils = require('../utils/login')
|
||||||
const podsUtils = require('../utils/pods')
|
const podsUtils = require('../utils/pods')
|
||||||
const serversUtils = require('../utils/servers')
|
const serversUtils = require('../utils/servers')
|
||||||
const usersUtils = require('../utils/users')
|
const usersUtils = require('../utils/users')
|
||||||
|
const requestsUtils = require('../utils/requests')
|
||||||
const videosUtils = require('../utils/videos')
|
const videosUtils = require('../utils/videos')
|
||||||
|
|
||||||
describe('Test users', function () {
|
describe('Test users', function () {
|
||||||
|
@ -138,6 +139,23 @@ describe('Test users', function () {
|
||||||
videosUtils.uploadVideo(server.url, accessToken, name, description, tags, video, 204, done)
|
videosUtils.uploadVideo(server.url, accessToken, name, description, tags, video, 204, done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should retrieve a video rating', function (done) {
|
||||||
|
videosUtils.rateVideo(server.url, accessToken, videoId, 'like', function (err) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
usersUtils.getUserVideoRating(server.url, accessToken, videoId, function (err, res) {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
const rating = res.body
|
||||||
|
|
||||||
|
expect(rating.videoId).to.equal(videoId)
|
||||||
|
expect(rating.rating).to.equal('like')
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('Should not be able to remove the video with an incorrect token', function (done) {
|
it('Should not be able to remove the video with an incorrect token', function (done) {
|
||||||
videosUtils.removeVideo(server.url, 'bad_token', videoId, 401, done)
|
videosUtils.removeVideo(server.url, 'bad_token', videoId, 401, done)
|
||||||
})
|
})
|
||||||
|
@ -150,10 +168,21 @@ describe('Test users', function () {
|
||||||
|
|
||||||
it('Should logout (revoke token)')
|
it('Should logout (revoke token)')
|
||||||
|
|
||||||
|
it('Should not be able to get the user informations')
|
||||||
|
|
||||||
it('Should not be able to upload a video')
|
it('Should not be able to upload a video')
|
||||||
|
|
||||||
it('Should not be able to remove a video')
|
it('Should not be able to remove a video')
|
||||||
|
|
||||||
|
it('Should not be able to rate a video', function (done) {
|
||||||
|
const path = '/api/v1/videos/'
|
||||||
|
const data = {
|
||||||
|
rating: 'likes'
|
||||||
|
}
|
||||||
|
|
||||||
|
requestsUtils.makePutBodyRequest(server.url, path + videoId, 'wrong token', data, done, 401)
|
||||||
|
})
|
||||||
|
|
||||||
it('Should be able to login again')
|
it('Should be able to login again')
|
||||||
|
|
||||||
it('Should have an expired access token')
|
it('Should have an expired access token')
|
||||||
|
|
|
@ -5,6 +5,7 @@ const request = require('supertest')
|
||||||
const usersUtils = {
|
const usersUtils = {
|
||||||
createUser,
|
createUser,
|
||||||
getUserInformation,
|
getUserInformation,
|
||||||
|
getUserVideoRating,
|
||||||
getUsersList,
|
getUsersList,
|
||||||
getUsersListPaginationAndSort,
|
getUsersListPaginationAndSort,
|
||||||
removeUser,
|
removeUser,
|
||||||
|
@ -47,6 +48,18 @@ function getUserInformation (url, accessToken, end) {
|
||||||
.end(end)
|
.end(end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getUserVideoRating (url, accessToken, videoId, end) {
|
||||||
|
const path = '/api/v1/users/me/videos/' + videoId + '/rating'
|
||||||
|
|
||||||
|
request(url)
|
||||||
|
.get(path)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.set('Authorization', 'Bearer ' + accessToken)
|
||||||
|
.expect(200)
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.end(end)
|
||||||
|
}
|
||||||
|
|
||||||
function getUsersList (url, end) {
|
function getUsersList (url, end) {
|
||||||
const path = '/api/v1/users'
|
const path = '/api/v1/users'
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,8 @@ const videosUtils = {
|
||||||
searchVideoWithSort,
|
searchVideoWithSort,
|
||||||
testVideoImage,
|
testVideoImage,
|
||||||
uploadVideo,
|
uploadVideo,
|
||||||
updateVideo
|
updateVideo,
|
||||||
|
rateVideo
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------- Export functions --------------------
|
// ---------------------- Export functions --------------------
|
||||||
|
@ -236,6 +237,23 @@ function updateVideo (url, accessToken, id, name, description, tags, specialStat
|
||||||
req.expect(specialStatus).end(end)
|
req.expect(specialStatus).end(end)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rateVideo (url, accessToken, id, rating, specialStatus, end) {
|
||||||
|
if (!end) {
|
||||||
|
end = specialStatus
|
||||||
|
specialStatus = 204
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = '/api/v1/videos/' + id + '/rate'
|
||||||
|
|
||||||
|
request(url)
|
||||||
|
.put(path)
|
||||||
|
.set('Accept', 'application/json')
|
||||||
|
.set('Authorization', 'Bearer ' + accessToken)
|
||||||
|
.send({ rating })
|
||||||
|
.expect(specialStatus)
|
||||||
|
.end(end)
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
module.exports = videosUtils
|
module.exports = videosUtils
|
||||||
|
|
Loading…
Reference in New Issue