Skip to content

Commit

Permalink
Merge branch 'master' into feature/disable-login-button-color
Browse files Browse the repository at this point in the history
  • Loading branch information
ritvje committed Jul 21, 2016
2 parents a7058ff + 7ab4fb4 commit 46be025
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/client/actions/RegistryUserActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function getRegistryUserActions(alt, registryUserResource, errorActions)
loadRegistryUserList() {
return dispatch => {
dispatch();
registryUserResource.findAll()
registryUserResource.findAll('includePresence=true')
.then(registryUserList => this.registryUserListUpdated(registryUserList),
err => errorActions.error(err, 'Käyttäjiä ei voitu ladata'));
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { Table, Button } from 'react-bootstrap';
import { Presence } from '../../components';

const RegistryUserRow = props => {
const {
Expand All @@ -15,6 +16,7 @@ const RegistryUserRow = props => {
phoneNumber,
email,
status,
presence,
} = registryUser;

const blockStatusToggleButton = status === 'blocked'
Expand All @@ -23,6 +25,7 @@ const RegistryUserRow = props => {

return (
<tr>
<td><Presence value={ presence } /></td>
<td>{ `${firstName} ${lastName}` }</td>
<td>{ memberNumber }</td>
<td>{ phoneNumber }</td>
Expand All @@ -49,6 +52,7 @@ export function RegistryUserTable(props) {
<Table striped responsive condensed>
<thead>
<tr>
<th>Tila</th>
<th>Nimi</th>
<th>Jäsennumero</th>
<th>Puhelinnumero</th>
Expand Down
55 changes: 34 additions & 21 deletions src/common/models/participant.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,19 +314,15 @@ export default function (Participant) {

Participant.getParticipantInformationForApp = (memberNumber, email, cb) => {
const findParticipant = Promise.promisify(Participant.findOne, { context: Participant });
let err;
let where;

if (!memberNumber && !email) {
err = new Error('email or memberNumber is required!');
err.status = 400;
return cb(err);
} else {
if (memberNumber) {
where = { memberNumber: memberNumber };
} else if (email) {
where = { email: email };
Promise.try(() => {
if (!(memberNumber || email)) {
const err = new Error('email or memberNumber is required!');
err.status = 400;
throw err;
}

return memberNumber ? { memberNumber: memberNumber } : { email: email };
}).then(where =>
findParticipant({
where: where,
fields: [
Expand All @@ -341,17 +337,26 @@ export default function (Participant) {
'memberNumber',
'email',
],
}).then(participant => {
if (!participant) {
err = new Error('Participant not found');
err.status = 404;
throw err;
} else {
return participant;
}
}).asCallback(cb);
}
)).then(participant => {
if (!participant) {
const err = new Error('Participant not found');
err.status = 404;
throw err;
} else {
return participant;
}
}).asCallback(cb);
};

Participant.participantAmount = (subCamp, cb) => {
const countParticipants = Promise.promisify(Participant.count, { context: Participant });
const filter = { presence: 3 };
if (subCamp) {
filter.subCamp = subCamp;
}

countParticipants(filter).asCallback(cb);
};

Participant.remoteMethod('massAssignField',
Expand All @@ -376,4 +381,12 @@ export default function (Participant) {
returns: { type: 'object', root: true },
}
);

Participant.remoteMethod('participantAmount',
{
http: { path: '/participantAmount', verb: 'get' },
accepts: { arg: 'subCamp', type: 'string', required: false },
returns: { arg: 'amount', type: 'string' },
}
);
}
7 changes: 7 additions & 0 deletions src/common/models/participant.json
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,13 @@
"principalId": "registryUser",
"permission": "DENY",
"property": "getParticipantInformationForApp"
},
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "participantAmount"
}
],
"methods": {}
Expand Down
25 changes: 25 additions & 0 deletions src/common/models/registryuser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import app from '../../server/server.js';
import Promise from 'bluebird';
import loopback from 'loopback';
import crypto from 'crypto';
import _ from 'lodash';

export default function(Registryuser) {
Registryuser.afterRemote('create', (ctx, registryuserInstance, next) => {
Expand Down Expand Up @@ -61,6 +62,30 @@ export default function(Registryuser) {
updateRegistryUser({ id: userId }, { status: null }).asCallback(callback);
};

Registryuser.afterRemote('find', (ctx, instance, next) => {
Promise.try(() => {
const includePresence = ctx && ctx.result && ctx.req && ctx.req.query && ctx.req.query.includePresence;
if (includePresence) {
const findParticipants = Promise.promisify(app.models.Participant.find, { context: app.models.Participant });

const memberNumbers = ctx.result.map(user => user.memberNumber);

// To object is an undocumented function of loopback models, which is used here to force the model object into a plain javascript object.
// Without the call setting a new property on the objects below wouldn't work.
const keyedUsers = _.keyBy(ctx.result.map(user => user.toObject()), 'memberNumber');

return findParticipants({ where: { memberNumber: { inq: memberNumbers } } })
.each(participant => {
const user = keyedUsers[participant.memberNumber];
if (user) {
user.presence = participant.presence;
}
})
.tap(() => ctx.result = _.values(keyedUsers));
}
}).asCallback(next);
});

Registryuser.remoteMethod('block',
{
http: { path: '/:id/block', verb: 'post' },
Expand Down
5 changes: 5 additions & 0 deletions test/acl-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ describe('http api access control', () => {

it('appInformation: UNAUTHORIZED', () => get('/api/participants/appInformation?memberNumber=1234').expect(UNAUTHORIZED));
it('massedit: UNAUTHORIZED', () => post('/api/participants/massAssign', { ids: [1], newValue: 1, fieldName: 'presence' }).expect(UNAUTHORIZED));
it('participantAmount: OK', () => get('/api/participants/participantAmount').expect(OK));
});

describe('Authenticated user without roles', () => {
Expand All @@ -325,6 +326,7 @@ describe('http api access control', () => {

it('appInformation: UNAUTHORIZED', () => get('/api/participants/appInformation?memberNumber=1234', noRolesAccessToken).expect(UNAUTHORIZED));
it('massedit: UNAUTHORIZED', () => post('/api/participants/massAssign', { ids: [1], newValue: 1, fieldName: 'presence' }, noRolesAccessToken).expect(UNAUTHORIZED));
it('participantAmount: OK', () => get('/api/participants/participantAmount', noRolesAccessToken).expect(OK));
});

describe('registryUser', () => {
Expand All @@ -342,6 +344,7 @@ describe('http api access control', () => {

it('appInformation: UNAUTHORIZED', () => get('/api/participants/appInformation?memberNumber=1234', registryUserAccessToken).expect(UNAUTHORIZED));
it('massedit: UNAUTHORIZED', () => post('/api/participants/massAssign', { ids: [1], newValue: 1, fieldName: 'presence' }, registryUserAccessToken).expect(OK));
it('participantAmount: OK', () => get('/api/participants/participantAmount', registryUserAccessToken).expect(OK));
});

describe('registryAdmin', () => {
Expand All @@ -359,6 +362,7 @@ describe('http api access control', () => {

it('appInformation: UNAUTHORIZED', () => get('/api/participants/appInformation?memberNumber=1234', registryAdminAccessToken).expect(UNAUTHORIZED));
it('massedit: UNAUTHORIZED', () => post('/api/participants/massAssign', { ids: [1], newValue: 1, fieldName: 'presence' }, registryAdminAccessToken).expect(UNAUTHORIZED));
it('participantAmount: OK', () => get('/api/participants/participantAmount', registryAdminAccessToken).expect(OK));
});

describe('roihuapp user', () => {
Expand All @@ -376,6 +380,7 @@ describe('http api access control', () => {

it('appInformation: OK', () => get('/api/participants/appInformation?memberNumber=1234', roihuappUserAccessToken).expect(OK));
it('massedit: UNAUTHORIZED', () => post('/api/participants/massAssign', { ids: [1], newValue: 1, fieldName: 'presence' }, roihuappUserAccessToken).expect(UNAUTHORIZED));
it('participantAmount: OK', () => get('/api/participants/participantAmount', roihuappUserAccessToken).expect(OK));
});
});

Expand Down
84 changes: 84 additions & 0 deletions test/participant-amount-endpoint.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import app from '../src/server/server';
import request from 'supertest-as-promised';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import * as testUtils from './utils/test-utils';
import { resetDatabase } from '../scripts/seed-database';

const expect = chai.expect;
chai.use(chaiAsPromised);

describe('Participant amount endpoint test', () => {
const testParticipants = [
{
'participantId': 1,
'firstName': 'Teemu',
'lastName': 'Testihenkilö',
'nonScout': false,
'internationalGuest': false,
'localGroup': 'Testilippukunta',
'campGroup': 'Leirilippukunta',
'village': 'Testikylä',
'subCamp': 'Humina',
'ageGroup': 'sudenpentu',
'memberNumber': 123,
'presence': 3,
},
{
'participantId': 2,
'firstName': 'Tero',
'lastName': 'Esimerkki',
'nonScout': false,
'internationalGuest': false,
'localGroup': 'Testilippukunta',
'campGroup': 'Leirilippukunta',
'village': 'Testikylä',
'subCamp': 'Hurma',
'ageGroup': 'sudenpentu',
'memberNumber': 345,
'presence': 3,
},
{
'participantId': 3,
'firstName': 'Jussi',
'lastName': 'Jukola',
'nonScout': false,
'internationalGuest': false,
'localGroup': 'Testilippukunta',
'campGroup': 'Leirilippukunta',
'village': 'Testikylä',
'subCamp': 'Humina',
'ageGroup': 'seikkailija',
'memberNumber': 859,
'presence': 2,
},
];

beforeEach(() =>
resetDatabase()
.then(() => testUtils.createFixture('Participant', testParticipants))
);

function getParticipantAmount(subCamp) {
const req = request(app)
.get(`/api/Participants/participantAmount`);
if (subCamp) {
req.set('subCamp', subCamp);
}
return req;
}

it('should give amount of present participants in whole camp', () =>
getParticipantAmount().then(res => {
expect(200);
expect(res.body).to.have.property('amount', 2);
})
);

it('should give amount of present participants in one sub camp', () =>
getParticipantAmount('Humina').then(res => {
expect(200);
expect(res.body).to.have.property('amount', 1);
})
);
});

0 comments on commit 46be025

Please sign in to comment.