[IMPROVE] Replace livechat:visitorHistory publication by REST (#15943)

* Replace livechat:visitorHistory publication by REST

* Fix wrong helper name

* Apply suggestions from review

* Fix isLoading ReactiveVar.
pull/15957/head
Marcos Spessatto Defendi 6 years ago committed by Renato Becker
parent c3860bbd03
commit 3c76440000
  1. 18
      app/livechat/client/views/app/tabbar/visitorHistory.html
  2. 56
      app/livechat/client/views/app/tabbar/visitorHistory.js
  3. 26
      app/livechat/imports/server/rest/visitors.js
  4. 33
      app/livechat/server/api/lib/visitors.js
  5. 1
      app/livechat/server/publications/visitorHistory.js
  6. 9
      app/models/server/raw/LivechatRooms.js
  7. 45
      tests/end-to-end/api/livechat/visitors.js

@ -5,13 +5,17 @@
<h2>{{_ "Past_Chats"}}</h2>
</div>
<div class="visitor-navigation">
<div class="visitor-scroll">
<ul>
{{#each previousChats}}
<li><a href="{{pathFor "live" id=_id}}">{{title}}</a></li>
{{/each}}
</ul>
</div>
{{#if isLoading}}
{{_ "Loading..."}}
{{else}}
<div class="visitor-scroll">
<ul>
{{#each previousChats}}
<li><a href="{{pathFor "live" id=_id}}">{{title}}</a></li>
{{/each}}
</ul>
</div>
{{/if}}
</div>
</div>
</div>

@ -1,27 +1,20 @@
import { ReactiveVar } from 'meteor/reactive-var';
import { Template } from 'meteor/templating';
import { Mongo } from 'meteor/mongo';
import moment from 'moment';
import _ from 'underscore';
import { ChatRoom } from '../../../../../models';
import './visitorHistory.html';
import { APIClient } from '../../../../../utils/client';
const visitorHistory = new Mongo.Collection('visitor_history');
const ITEMS_COUNT = 50;
Template.visitorHistory.helpers({
historyLoaded() {
return !Template.instance().loadHistory.ready();
isLoading() {
return Template.instance().isLoading.get();
},
previousChats() {
return visitorHistory.find({
_id: { $ne: this.rid },
'v._id': Template.instance().visitorId.get(),
}, {
sort: {
ts: -1,
},
});
return Template.instance().history.get();
},
title() {
@ -38,15 +31,40 @@ Template.visitorHistory.helpers({
Template.visitorHistory.onCreated(function() {
const currentData = Template.currentData();
this.visitorId = new ReactiveVar();
this.isLoading = new ReactiveVar(false);
this.history = new ReactiveVar([]);
this.offset = new ReactiveVar(0);
this.total = new ReactiveVar(0);
this.autorun(() => {
const room = ChatRoom.findOne({ _id: Template.currentData().rid });
if (room) {
this.autorun(async () => {
const { room } = await APIClient.v1.get(`rooms.info?roomId=${ currentData.rid }`);
if (room && room.v) {
this.visitorId.set(room.v._id);
}
});
if (currentData && currentData.rid) {
this.loadHistory = this.subscribe('livechat:visitorHistory', { rid: currentData.rid });
}
this.autorun(async () => {
if (!this.visitorId.get() || !currentData || !currentData.rid) {
return;
}
const offset = this.offset.get();
this.isLoading.set(true);
const { history, total } = await APIClient.v1.get(`livechat/visitors.chatHistory/room/${ currentData.rid }/visitor/${ this.visitorId.get() }?count=${ ITEMS_COUNT }&offset=${ offset }`);
this.isLoading.set(false);
this.total.set(total);
this.history.set(this.history.get().concat(history));
});
});
Template.visitorHistory.events({
'scroll .visitor-scroll': _.throttle(function(e, instance) {
if (e.target.scrollTop >= (e.target.scrollHeight - e.target.clientHeight)) {
const history = instance.history.get();
if (instance.total.get() <= history.length) {
return;
}
return instance.offset.set(instance.offset.get() + ITEMS_COUNT);
}
}, 200),
});

@ -2,7 +2,7 @@
import { check } from 'meteor/check';
import { API } from '../../../../api';
import { findVisitorInfo, findVisitedPages } from '../../../server/api/lib/visitors';
import { findVisitorInfo, findVisitedPages, findChatHistory } from '../../../server/api/lib/visitors';
API.v1.addRoute('livechat/visitors.info', { authRequired: true }, {
get() {
@ -38,3 +38,27 @@ API.v1.addRoute('livechat/visitors.pagesVisited/:roomId', { authRequired: true }
return API.v1.success(pages);
},
});
API.v1.addRoute('livechat/visitors.chatHistory/room/:roomId/visitor/:visitorId', { authRequired: true }, {
get() {
check(this.urlParams, {
visitorId: String,
roomId: String,
});
const { offset, count } = this.getPaginationItems();
const { sort } = this.parseJsonQuery();
const history = Promise.await(findChatHistory({
userId: this.userId,
roomId: this.urlParams.roomId,
visitorId: this.urlParams.visitorId,
pagination: {
offset,
count,
sort,
},
}));
return API.v1.success(history);
},
});

@ -1,5 +1,6 @@
import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission';
import { LivechatVisitors, Messages, LivechatRooms } from '../../../../models/server/raw';
import { canAccessRoomAsync } from '../../../../authorization/server/functions/canAccessRoom';
export async function findVisitorInfo({ userId, visitorId }) {
if (!await hasPermissionAsync(userId, 'view-l-room')) {
@ -24,7 +25,7 @@ export async function findVisitedPages({ userId, roomId, pagination: { offset, c
if (!room) {
throw new Error('invalid-room');
}
const cursor = await Messages.findByRoomIdAndType(room._id, 'livechat_navigation_history', {
const cursor = Messages.findByRoomIdAndType(room._id, 'livechat_navigation_history', {
sort: sort || { ts: -1 },
skip: offset,
limit: count,
@ -41,3 +42,33 @@ export async function findVisitedPages({ userId, roomId, pagination: { offset, c
total,
};
}
export async function findChatHistory({ userId, roomId, visitorId, pagination: { offset, count, sort } }) {
if (!await hasPermissionAsync(userId, 'view-l-room')) {
throw new Error('error-not-authorized');
}
const room = await LivechatRooms.findOneById(roomId);
if (!room) {
throw new Error('invalid-room');
}
if (!await canAccessRoomAsync(room, { _id: userId })) {
throw new Error('error-not-allowed');
}
const cursor = LivechatRooms.findByVisitorId(visitorId, {
sort: sort || { ts: -1 },
skip: offset,
limit: count,
});
const total = await cursor.count();
const history = await cursor.toArray();
return {
history,
count: history.length,
offset,
total,
};
}

@ -4,6 +4,7 @@ import { hasPermission } from '../../../authorization';
import { LivechatRooms, Subscriptions } from '../../../models';
Meteor.publish('livechat:visitorHistory', function({ rid: roomId }) {
console.warn('The publication "livechat:visitorHistory" is deprecated and will be removed after version v3.0.0');
if (!this.userId) {
return this.error(new Meteor.Error('error-not-authorized', 'Not authorized', { publish: 'livechat:visitorHistory' }));
}

@ -94,4 +94,13 @@ export class LivechatRoomsRaw extends BaseRaw {
}
return this.col.aggregate(params).toArray();
}
findByVisitorId(visitorId, options) {
const query = {
t: 'l',
'v._id': visitorId,
};
return this.find(query, options);
}
}

@ -101,4 +101,49 @@ describe('LIVECHAT - visitors', function() {
});
});
});
describe('livechat/visitors.chatHistory/room/room-id/visitor/visitor-id', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', (done) => {
updatePermission('view-l-room', []).then(() => {
request.get(api('livechat/visitors.chatHistory/room/room-id/visitor/visitor-id'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('error-not-authorized');
})
.end(done);
});
});
it('should return an "error" when the roomId param is invalid', (done) => {
updatePermission('view-l-room', ['admin']).then(() => {
request.get(api('livechat/visitors.chatHistory/room/room-id/visitor/visitor-id'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
});
it('should return an array of chat history', (done) => {
updatePermission('view-l-room', ['admin'])
.then(() => {
request.get(api(`livechat/visitors.chatHistory/room/GENERAL/visitor/${ visitor._id }`))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.history).to.be.an('array');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('count');
})
.end(done);
});
});
});
});

Loading…
Cancel
Save