[IMPROVE] Replace `livechat:departments` publication by REST Calls (#15478)

pull/15523/head^2
Marcos Spessatto Defendi 6 years ago committed by Diego Sampaio
parent 50589a3b1e
commit 4ac63d84ea
  1. 17
      app/livechat/client/views/app/livechatCurrentChats.js
  2. 25
      app/livechat/client/views/app/livechatDepartmentForm.js
  3. 24
      app/livechat/client/views/app/livechatDepartments.js
  4. 16
      app/livechat/client/views/app/livechatQueue.js
  5. 13
      app/livechat/client/views/app/tabbar/visitorForward.js
  6. 13
      app/livechat/client/views/app/tabbar/visitorInfo.js
  7. 47
      app/livechat/imports/server/rest/departments.js
  8. 40
      app/livechat/server/api/lib/departments.js
  9. 4
      app/models/server/raw/BaseRaw.js
  10. 5
      app/models/server/raw/LivechatDepartment.js
  11. 5
      app/models/server/raw/LivechatDepartmentAgents.js
  12. 6
      app/models/server/raw/index.js
  13. 22
      tests/data/livechat/department.js
  14. 123
      tests/end-to-end/api/livechat/01-department.js

@ -7,8 +7,7 @@ import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
import { modal, call, popover } from '../../../../ui-utils';
import { t } from '../../../../utils/client';
import { LivechatDepartment } from '../../collections/LivechatDepartment';
import { t, APIClient } from '../../../../utils/client';
import { LivechatRoom } from '../../collections/LivechatRoom';
import './livechatCurrentChats.html';
@ -52,15 +51,15 @@ Template.livechatCurrentChats.helpers({
onClickTagAgent() {
return Template.instance().onClickTagAgent;
},
departments() {
return LivechatDepartment.find({}, { sort: { name: 1 } });
},
customFilters() {
return Template.instance().customFilters.get();
},
tagFilters() {
return Template.instance().tagFilters.get();
},
departments() {
return Template.instance().departments.get();
},
tagId() {
return this;
},
@ -247,7 +246,7 @@ Template.livechatCurrentChats.events({
},
});
Template.livechatCurrentChats.onCreated(function() {
Template.livechatCurrentChats.onCreated(async function() {
this.ready = new ReactiveVar(false);
this.limit = new ReactiveVar(20);
this.filter = new ReactiveVar({});
@ -255,6 +254,7 @@ Template.livechatCurrentChats.onCreated(function() {
this.customFilters = new ReactiveVar([]);
this.customFields = new ReactiveVar([]);
this.tagFilters = new ReactiveVar([]);
this.departments = new ReactiveVar([]);
this.onSelectAgents = ({ item: agent }) => {
this.selectedAgents.set([agent]);
@ -264,11 +264,12 @@ Template.livechatCurrentChats.onCreated(function() {
this.selectedAgents.set(this.selectedAgents.get().filter((user) => user.username !== username));
};
this.autorun(() => {
this.autorun(async () => {
this.ready.set(this.subscribe('livechat:rooms', this.filter.get(), 0, this.limit.get()).ready());
});
this.subscribe('livechat:departments');
const { departments } = await APIClient.v1.get('livechat/department?sort={"name": 1}');
this.departments.set(departments);
Meteor.call('livechat:getCustomFields', (err, customFields) => {
if (customFields) {

@ -8,10 +8,9 @@ import toastr from 'toastr';
import { t, handleError } from '../../../../utils';
import { hasPermission } from '../../../../authorization';
import { AgentUsers } from '../../collections/AgentUsers';
import { LivechatDepartment } from '../../collections/LivechatDepartment';
import { LivechatDepartmentAgents } from '../../collections/LivechatDepartmentAgents';
import { getCustomFormTemplate } from './customTemplates/register';
import './livechatDepartmentForm.html';
import { APIClient } from '../../../../utils/client';
Template.livechatDepartmentForm.helpers({
department() {
@ -189,22 +188,12 @@ Template.livechatDepartmentForm.onCreated(function() {
this.subscribe('livechat:agents');
this.autorun(() => {
const sub = this.subscribe('livechat:departments', FlowRouter.getParam('_id'));
if (sub.ready()) {
const department = LivechatDepartment.findOne({ _id: FlowRouter.getParam('_id') });
if (department) {
this.department.set(department);
const { _id: departmentId } = department;
this.subscribe('livechat:departmentAgents', departmentId, () => {
const newSelectedAgents = [];
LivechatDepartmentAgents.find({ departmentId }).forEach((agent) => {
newSelectedAgents.push(agent);
});
this.selectedAgents.set(newSelectedAgents);
});
}
this.autorun(async () => {
const id = FlowRouter.getParam('_id');
if (id) {
const { department, agents } = await APIClient.v1.get(`livechat/department/${ FlowRouter.getParam('_id') }`);
this.department.set(department);
this.selectedAgents.set(agents);
}
});
});

@ -8,12 +8,12 @@ import _ from 'underscore';
import { modal } from '../../../../ui-utils';
import { t, handleError } from '../../../../utils';
import { LivechatDepartment } from '../../collections/LivechatDepartment';
import './livechatDepartments.html';
import { APIClient } from '../../../../utils/client';
Template.livechatDepartments.helpers({
departments() {
return Template.instance().departments();
return Template.instance().getDepartmentsWithCriteria();
},
isLoading() {
return Template.instance().state.get('loading');
@ -93,25 +93,21 @@ Template.livechatDepartments.onCreated(function() {
loading: false,
});
this.ready = new ReactiveVar(true);
this.departments = new ReactiveVar([]);
this.autorun(function() {
this.autorun(async function() {
const limit = instance.limit.get();
const subscription = instance.subscribe('livechat:departments', null, limit);
instance.ready.set(subscription.ready());
const { departments } = await APIClient.v1.get(`livechat/department?count=${ limit }`);
instance.departments.set(departments);
instance.ready.set(true);
});
this.departments = function() {
this.getDepartmentsWithCriteria = function() {
let filter;
let query = {};
if (instance.filter && instance.filter.get()) {
filter = s.trim(instance.filter.get());
}
if (filter) {
query = { name: new RegExp(s.escapeRegExp(filter), 'i') };
}
const limit = instance.limit && instance.limit.get();
return LivechatDepartment.find(query, { limit, sort: { name: 1 } }).fetch();
const regex = new RegExp(s.escapeRegExp(filter), 'i');
return instance.departments.get().filter((department) => department.name.match(regex));
};
});

@ -5,20 +5,14 @@ import { Template } from 'meteor/templating';
import { settings } from '../../../../settings';
import { hasPermission } from '../../../../authorization';
import { Users } from '../../../../models';
import { LivechatDepartment } from '../../collections/LivechatDepartment';
import { LivechatQueueUser } from '../../collections/LivechatQueueUser';
import { AgentUsers } from '../../collections/AgentUsers';
import './livechatQueue.html';
import { APIClient } from '../../../../utils/client';
Template.livechatQueue.helpers({
departments() {
return LivechatDepartment.find({
enabled: true,
}, {
sort: {
name: 1,
},
});
return Template.instance().departments.get().filter((department) => department.enabled === true);
},
users() {
@ -63,10 +57,12 @@ Template.livechatQueue.events({
},
});
Template.livechatQueue.onCreated(function() {
Template.livechatQueue.onCreated(async function() {
this.showOffline = new ReactiveVar({});
this.departments = new ReactiveVar([]);
this.subscribe('livechat:queue');
this.subscribe('livechat:agents');
this.subscribe('livechat:departments');
const { departments } = await APIClient.v1.get('livechat/department?sort={"name": 1}');
this.departments.set(departments);
});

@ -6,19 +6,19 @@ import toastr from 'toastr';
import { ChatRoom } from '../../../../../models';
import { t } from '../../../../../utils';
import { LivechatDepartment } from '../../../collections/LivechatDepartment';
import { AgentUsers } from '../../../collections/AgentUsers';
import './visitorForward.html';
import { APIClient } from '../../../../../utils/client';
Template.visitorForward.helpers({
visitor() {
return Template.instance().visitor.get();
},
hasDepartments() {
return LivechatDepartment.find({ enabled: true }).count() > 0;
return Template.instance().departments.get().filter((department) => department.enabled === true).length > 0;
},
departments() {
return LivechatDepartment.find({ enabled: true });
return Template.instance().departments.get().filter((department) => department.enabled === true);
},
agents() {
const query = {
@ -34,9 +34,10 @@ Template.visitorForward.helpers({
},
});
Template.visitorForward.onCreated(function() {
Template.visitorForward.onCreated(async function() {
this.visitor = new ReactiveVar();
this.room = new ReactiveVar();
this.departments = new ReactiveVar([]);
this.autorun(() => {
this.visitor.set(Meteor.users.findOne({ _id: Template.currentData().visitorId }));
@ -45,9 +46,9 @@ Template.visitorForward.onCreated(function() {
this.autorun(() => {
this.room.set(ChatRoom.findOne({ _id: Template.currentData().roomId }));
});
this.subscribe('livechat:departments');
this.subscribe('livechat:agents');
const { departments } = await APIClient.v1.get('livechat/department');
this.departments.set(departments);
});

@ -15,9 +15,9 @@ import { settings } from '../../../../../settings';
import { t, handleError, roomTypes } from '../../../../../utils';
import { hasRole, hasAllPermission, hasAtLeastOnePermission } from '../../../../../authorization';
import { LivechatVisitor } from '../../../collections/LivechatVisitor';
import { LivechatDepartment } from '../../../collections/LivechatDepartment';
import { LivechatRoom } from '../../../collections/LivechatRoom';
import './visitorInfo.html';
import { APIClient } from '../../../../../utils/client';
const isSubscribedToRoom = () => {
const data = Template.currentData();
@ -55,7 +55,7 @@ Template.visitorInfo.helpers({
},
department() {
return LivechatDepartment.findOne({ _id: Template.instance().departmentId.get() });
return Template.instance().department.get();
},
joinTags() {
@ -283,6 +283,7 @@ Template.visitorInfo.onCreated(function() {
this.departmentId = new ReactiveVar(null);
this.tags = new ReactiveVar(null);
this.routingConfig = new ReactiveVar({});
this.department = new ReactiveVar({});
Meteor.call('livechat:getCustomFields', (err, customFields) => {
if (customFields) {
@ -307,9 +308,15 @@ Template.visitorInfo.onCreated(function() {
});
this.subscribe('livechat:visitorInfo', { rid });
this.subscribe('livechat:departments', { _id: this.departmentId.get() });
}
this.autorun(async () => {
if (this.departmentId.get()) {
const { department } = await APIClient.v1.get(`livechat/department/${ this.departmentId.get() }?includeAgents=false`);
this.department.set(department);
}
});
this.autorun(() => {
this.user.set(LivechatVisitor.findOne({ _id: this.visitorId.get() }));
});

@ -4,16 +4,23 @@ import { API } from '../../../../api';
import { hasPermission } from '../../../../authorization';
import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../models';
import { Livechat } from '../../../server/lib/Livechat';
import { findDepartments, findDepartmentById } from '../../../server/api/lib/departments';
API.v1.addRoute('livechat/department', { authRequired: true }, {
get() {
if (!hasPermission(this.userId, 'view-livechat-departments')) {
return API.v1.unauthorized();
}
return API.v1.success({
departments: LivechatDepartment.find().fetch(),
});
const { offset, count } = this.getPaginationItems();
const { sort } = this.parseJsonQuery();
const departments = Promise.await(findDepartments({
userId: this.userId,
pagination: {
offset,
count,
sort,
},
}));
return API.v1.success(departments);
},
post() {
if (!hasPermission(this.userId, 'manage-livechat-departments')) {
@ -44,22 +51,22 @@ API.v1.addRoute('livechat/department', { authRequired: true }, {
API.v1.addRoute('livechat/department/:_id', { authRequired: true }, {
get() {
if (!hasPermission(this.userId, 'view-livechat-departments')) {
return API.v1.unauthorized();
}
check(this.urlParams, {
_id: String,
});
try {
check(this.urlParams, {
_id: String,
});
const { department, agents } = Promise.await(findDepartmentById({
userId: this.userId,
departmentId: this.urlParams._id,
includeAgents: this.queryParams.includeAgents && this.queryParams.includeAgents === 'true',
}));
return API.v1.success({
department: LivechatDepartment.findOneById(this.urlParams._id),
agents: LivechatDepartmentAgents.find({ departmentId: this.urlParams._id }).fetch(),
});
} catch (e) {
return API.v1.failure(e.error);
const result = { department };
if (agents) {
result.agents = agents;
}
return API.v1.success(result);
},
put() {
const permissionToSave = hasPermission(this.userId, 'manage-livechat-departments');

@ -0,0 +1,40 @@
import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission';
import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../models/server/raw';
export async function findDepartments({ userId, pagination: { offset, count, sort } }) {
if (!await hasPermissionAsync(userId, 'view-livechat-departments') && !await hasPermissionAsync(userId, 'view-l-room')) {
throw new Error('error-not-authorized');
}
const cursor = LivechatDepartment.find({}, {
sort: sort || { name: 1 },
skip: offset,
limit: count,
});
const total = await cursor.count();
const departments = await cursor.toArray();
return {
departments,
count: departments.length,
offset,
total,
};
}
export async function findDepartmentById({ userId, departmentId, includeAgents = true }) {
const canViewLivechatDepartments = await hasPermissionAsync(userId, 'view-livechat-departments');
if (!canViewLivechatDepartments && !await hasPermissionAsync(userId, 'view-l-room')) {
throw new Error('error-not-authorized');
}
const result = {
department: await LivechatDepartment.findOneById(departmentId),
};
if (includeAgents && canViewLivechatDepartments) {
result.agents = await LivechatDepartmentAgents.find({ departmentId }).toArray();
}
return result;
}

@ -11,6 +11,10 @@ export class BaseRaw {
return this.col.findOne(...args);
}
find(...args) {
return this.col.find(...args);
}
findUsersInRoles() {
throw new Error('overwrite-function', 'You must overwrite this function in the extended classes');
}

@ -0,0 +1,5 @@
import { BaseRaw } from './BaseRaw';
export class LivechatDepartmentRaw extends BaseRaw {
}

@ -0,0 +1,5 @@
import { BaseRaw } from './BaseRaw';
export class LivechatDepartmentAgentsRaw extends BaseRaw {
}

@ -10,6 +10,10 @@ import UsersModel from '../models/Users';
import { UsersRaw } from './Users';
import RoomsModel from '../models/Rooms';
import { RoomsRaw } from './Rooms';
import LivechatDepartmentModel from '../models/LivechatDepartment';
import { LivechatDepartmentRaw } from './LivechatDepartment';
import LivechatDepartmentAgentsModel from '../models/LivechatDepartmentAgents';
import { LivechatDepartmentAgentsRaw } from './LivechatDepartmentAgents';
export const Permissions = new PermissionsRaw(PermissionsModel.model.rawCollection());
export const Roles = new RolesRaw(RolesModel.model.rawCollection());
@ -17,3 +21,5 @@ export const Subscriptions = new SubscriptionsRaw(SubscriptionsModel.model.rawCo
export const Settings = new SettingsRaw(SettingsModel.model.rawCollection());
export const Users = new UsersRaw(UsersModel.model.rawCollection());
export const Rooms = new RoomsRaw(RoomsModel.model.rawCollection());
export const LivechatDepartment = new LivechatDepartmentRaw(LivechatDepartmentModel.model.rawCollection());
export const LivechatDepartmentAgents = new LivechatDepartmentAgentsRaw(LivechatDepartmentAgentsModel.model.rawCollection());

@ -0,0 +1,22 @@
import { api, credentials, request } from '../api-data';
export const createDepartment = () => new Promise((resolve, reject) => {
request.post(api('livechat/department'))
.send({
department: {
enabled: false,
email: 'email@email.com',
showOnRegistration: true,
showOnOfflineForm: true,
name: `new department ${ Date.now() }`,
description: 'created from api',
},
})
.set(credentials)
.end((err, res) => {
if (err) {
return reject(err);
}
resolve(res.body.department);
});
});

@ -0,0 +1,123 @@
import { getCredentials, api, request, credentials } from '../../../data/api-data.js';
import { updatePermission, updateSetting } from '../../../data/permissions.helper';
import { createDepartment } from '../../../data/livechat/department.js';
describe('LIVECHAT - departments', function() {
this.retries(0);
let department;
before((done) => getCredentials(done));
before((done) => {
updateSetting('Livechat_enabled', true)
.then(() => createDepartment())
.then((createdDepartment) => {
department = createdDepartment;
done();
}).catch(console.log);
});
describe('livechat/department', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission ["view-livechat-departments", "view-l-room"]', (done) => {
updatePermission('view-l-room', [])
.then(() => updatePermission('view-livechat-departments', []))
.then(() => {
request.get(api('livechat/department'))
.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 array of departments', (done) => {
updatePermission('view-l-room', ['admin'])
.then(() => updatePermission('view-livechat-departments', ['admin']))
.then(() => {
request.get(api('livechat/department'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.departments).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);
});
});
});
describe('livechat/department/id', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission ["view-livechat-departments", "view-l-room"]', (done) => {
updatePermission('view-l-room', [])
.then(() => updatePermission('view-livechat-departments', []))
.then(() => {
request.get(api(`livechat/department/${ department._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 the created department without the agents if the user does not have the necessary permission', (done) => {
updatePermission('view-l-room', ['admin'])
.then(() => updatePermission('view-livechat-departments', []))
.then(() => {
request.get(api(`livechat/department/${ department._id }`))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('department');
expect(res.body).to.not.have.property('agents');
expect(res.body.department._id).to.be.equal(department._id);
})
.end(done);
});
});
it('should return the created department without the agents if the user does have the permission but request to no include the agents', (done) => {
updatePermission('view-livechat-departments', ['admin'])
.then(() => {
request.get(api(`livechat/department/${ department._id }?includeAgents=false`))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('department');
expect(res.body).to.not.have.property('agents');
expect(res.body.department._id).to.be.equal(department._id);
})
.end(done);
});
});
it('should return the created department', (done) => {
updatePermission('view-l-room', ['admin'])
.then(() => updatePermission('view-livechat-departments', ['admin']))
.then(() => {
request.get(api(`livechat/department/${ department._id }`))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('department');
expect(res.body).to.have.property('agents');
expect(res.body.department._id).to.be.equal(department._id);
})
.end(done);
});
});
});
});
Loading…
Cancel
Save