Skip to content

Commit 9f6d257

Browse files
BrRenatjenyapoyarkov
authored andcommitted
fix: move member organizations to user metadata (#416)
* fix: move member organizations to user metadata * fix: fix tests * fix: added parse member data * fix: fix lint error * fix: fix members invite action * fix: fix invite test
1 parent cb37024 commit 9f6d257

9 files changed

Lines changed: 57 additions & 45 deletions

File tree

src/actions/organization/delete.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const {
88
ORGANIZATIONS_METADATA,
99
ORGANIZATIONS_MEMBERS,
1010
ORGANIZATIONS_NAME_TO_ID,
11-
USERS_ORGANIZATIONS,
11+
USERS_METADATA,
1212
ORGANIZATIONS_INDEX,
1313
ORGANIZATIONS_NAME_FIELD,
1414
} = require('../../constants');
@@ -41,7 +41,7 @@ async function deleteOrganization({ params }) {
4141
if (organizationMembersIds) {
4242
organizationMembersIds.forEach((memberId) => {
4343
pipeline.del(memberId);
44-
pipeline.hdel(redisKey(memberId.split('!').pop(), USERS_ORGANIZATIONS), organizationId);
44+
pipeline.hdel(redisKey(memberId.split('!').pop(), USERS_METADATA, audience), organizationId);
4545
});
4646
pipeline.del(organizationMembersListKey);
4747
}

src/actions/organization/invites/accept.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const {
77
ErrorInvitationExpiredOrUsed,
88
} = require('../../../constants');
99
const redisKey = require('../../../utils/key.js');
10+
const getUserId = require('../../../utils/userData/getUserId');
1011

1112
/**
1213
* Token verification function, on top of it returns extra metadata
@@ -43,7 +44,8 @@ async function acceptOrganizationMember({ params }) {
4344
const { redis } = this;
4445
const { username, organizationId } = params;
4546

46-
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, username);
47+
const userId = await getUserId.call(this, username);
48+
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId);
4749
const userInOrganization = await redis.hget(memberKey, 'username');
4850
if (!userInOrganization) {
4951
throw ErrorUserNotMember;

src/actions/organization/invites/send.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const {
1010
ORGANIZATIONS_ID_FIELD,
1111
USERS_ACTION_ORGANIZATION_INVITE,
1212
} = require('../../../constants');
13+
const getUserId = require('../../../utils/userData/getUserId');
1314

1415
/**
1516
* @api {amqp} <prefix>.invites.send Send invitation
@@ -31,7 +32,8 @@ async function sendOrganizationInvite({ params }) {
3132
const service = this;
3233
const { member, organizationId } = params;
3334

34-
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, member.email);
35+
const userId = await getUserId.call(this, member.email);
36+
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId);
3537
const userInOrganization = await service.redis.hget(memberKey, 'username');
3638
if (!userInOrganization) {
3739
throw ErrorUserNotMember;
Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
const { ActionTransport } = require('@microfleet/core');
2+
const union = require('lodash/union');
3+
const difference = require('lodash/difference');
24
const { checkOrganizationExists } = require('../../../utils/organization');
35
const redisKey = require('../../../utils/key');
4-
const { ErrorUserNotMember, ORGANIZATIONS_MEMBERS } = require('../../../constants');
6+
const handlePipeline = require('../../../utils/pipelineError');
7+
const getUserId = require('../../../utils/userData/getUserId');
8+
const { ErrorUserNotMember, USERS_METADATA, ORGANIZATIONS_MEMBERS } = require('../../../constants');
59

610
/**
711
* @api {amqp} <prefix>.members.permission Sets permission levels for a given user
@@ -16,35 +20,33 @@ const { ErrorUserNotMember, ORGANIZATIONS_MEMBERS } = require('../../../constant
1620
* @apiParam (Payload) {Object} permission - metadata operations,
1721
* supports `$set string[]`, `$remove string[]`
1822
*/
19-
async function addOrganizationMember({ params }) {
20-
const service = this;
21-
const { redis } = service;
23+
async function setOrganizationMemberPermission({ params }) {
24+
const { redis, config } = this;
2225
const { organizationId, username, permission } = params;
26+
const { audience } = config.organizations;
2327

24-
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, username);
25-
const userInOrganization = await redis.hget(memberKey, 'username');
26-
if (!userInOrganization) {
28+
const userId = await getUserId.call(this, username);
29+
const memberMetadataKey = redisKey(userId, USERS_METADATA, audience);
30+
const userPermissions = await redis.hget(memberMetadataKey, organizationId);
31+
if (!userPermissions) {
2732
throw ErrorUserNotMember;
2833
}
2934

30-
const currentPermissions = await redis.hget(memberKey, 'permissions');
31-
let permissions = currentPermissions === '' ? [] : currentPermissions.split(',');
35+
let permissions = userPermissions.length ? [] : JSON.parse(userPermissions);
3236

3337
const { $set = [], $remove = [] } = permission;
3438

35-
for (const permissionItem of $set) {
36-
if (!permissions.includes(permissionItem)) {
37-
permissions.push(permissionItem);
38-
}
39-
}
39+
permissions = union(permissions, $set);
40+
permissions = difference(permissions, $remove);
41+
permissions = JSON.stringify(permissions);
4042

41-
for (const permissionItem of $remove) {
42-
permissions = permissions.filter(item => item !== permissionItem);
43-
}
43+
const pipeline = redis.pipeline();
44+
pipeline.hset(memberMetadataKey, organizationId, permissions);
45+
pipeline.hset(redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId), 'permissions', permissions);
4446

45-
return redis.hset(memberKey, 'permissions', permissions.join(','));
47+
return pipeline.exec().then(handlePipeline);
4648
}
4749

48-
addOrganizationMember.allowed = checkOrganizationExists;
49-
addOrganizationMember.transports = [ActionTransport.amqp, ActionTransport.internal];
50-
module.exports = addOrganizationMember;
50+
setOrganizationMemberPermission.allowed = checkOrganizationExists;
51+
setOrganizationMemberPermission.transports = [ActionTransport.amqp, ActionTransport.internal];
52+
module.exports = setOrganizationMemberPermission;

src/actions/organization/members/remove.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
const { ActionTransport } = require('@microfleet/core');
22
const redisKey = require('../../../utils/key');
3+
const getUserId = require('../../../utils/userData/getUserId');
34
const handlePipeline = require('../../../utils/pipelineError');
45
const { checkOrganizationExists } = require('../../../utils/organization');
56
const {
67
ORGANIZATIONS_MEMBERS,
7-
USERS_ORGANIZATIONS,
8+
USERS_METADATA,
89
ErrorUserNotMember,
910
} = require('../../../constants');
1011

@@ -20,19 +21,21 @@ const {
2021
* @apiParam (Payload) {String} username - member email.
2122
*/
2223
async function removeMember({ params }) {
23-
const { redis } = this;
24+
const { redis, config } = this;
2425
const { organizationId, username } = params;
26+
const { audience } = config.organizations;
2527

26-
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, username);
27-
const userInOrganization = await redis.hget(memberKey, 'username');
28+
const userId = await getUserId.call(this, username);
29+
const memberKey = redisKey(userId, USERS_METADATA, audience);
30+
const userInOrganization = await redis.hget(memberKey, organizationId);
2831
if (!userInOrganization) {
2932
throw ErrorUserNotMember;
3033
}
3134

3235
const pipeline = redis.pipeline();
3336
pipeline.del(memberKey);
34-
pipeline.hdel(redisKey(username, USERS_ORGANIZATIONS), organizationId);
3537
pipeline.zrem(redisKey(organizationId, ORGANIZATIONS_MEMBERS), memberKey);
38+
pipeline.hdel(memberKey, organizationId);
3639

3740
return pipeline.exec().then(handlePipeline);
3841
}

src/utils/organization/addOrganizationMembers.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable no-mixed-operators */
22
const Promise = require('bluebird');
3+
const mapValues = require('lodash/mapValues');
34
const redisKey = require('../key.js');
45
const getUserId = require('../userData/getUserId');
56
const sendInviteMail = require('./sendInviteMail');
@@ -8,21 +9,23 @@ const registerOrganizationMembers = require('./registerOrganizationMembers');
89
const handlePipeline = require('../pipelineError.js');
910
const {
1011
ORGANIZATIONS_MEMBERS,
11-
USERS_ORGANIZATIONS,
12+
USERS_METADATA,
1213
ORGANIZATIONS_NAME_FIELD,
1314
USERS_ACTION_ORGANIZATION_INVITE,
1415
USERS_ACTION_ORGANIZATION_REGISTER,
1516
ORGANIZATIONS_ID_FIELD,
1617
} = require('../../constants.js');
1718

19+
const JSONStringify = data => JSON.stringify(data);
20+
1821
/**
1922
* Updates metadata on a organization object
2023
* @param {Object} opts
2124
* @return {Promise}
2225
*/
2326
async function addOrganizationMembers(opts) {
2427
const { redis } = this;
25-
const { organizationId, members } = opts;
28+
const { organizationId, members, audience } = opts;
2629

2730
const registeredMembers = [];
2831
const notRegisteredMembers = [];
@@ -42,15 +45,16 @@ async function addOrganizationMembers(opts) {
4245
const membersKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS);
4346
const organizationMembers = registeredMembers.concat(createdMembers);
4447
organizationMembers.forEach(({ password, ...member }) => {
45-
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, member.email);
46-
const memberOrganizations = redisKey(member.email, USERS_ORGANIZATIONS);
48+
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, member.id);
49+
const memberOrganizations = redisKey(member.id, USERS_METADATA, audience);
4750
member.username = member.email;
4851
member.invited = Date.now();
4952
member.accepted = password ? Date.now() : null;
5053
member.permissions = member.permissions || [];
51-
pipe.hmset(memberKey, member);
52-
pipe.hset(memberOrganizations, organizationId, JSON.stringify(member.permissions));
53-
pipe.zadd(membersKey, member.invited, memberKey);
54+
const stringifyMember = mapValues(member, JSONStringify);
55+
pipe.hmset(memberKey, stringifyMember);
56+
pipe.hset(memberOrganizations, organizationId, stringifyMember.permissions);
57+
pipe.zadd(membersKey, stringifyMember.invited, memberKey);
5458
});
5559

5660
await pipe.exec().then(handlePipeline);
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
const Promise = require('bluebird');
2+
const mapValues = require('lodash/mapValues');
23
const { ORGANIZATIONS_MEMBERS } = require('../../constants');
34
const redisKey = require('../key');
45

6+
const JSONParse = d => JSON.parse(d);
7+
58
async function getOrganizationMembers(organizationId) {
69
const { redis } = this;
710
const organizationMembersIds = await redis.zrange(redisKey(organizationId, ORGANIZATIONS_MEMBERS), 0, -1);
@@ -11,11 +14,7 @@ async function getOrganizationMembers(organizationId) {
1114
}
1215

1316
const members = await Promise.all(organizationMembersJobs);
14-
15-
return members.map(member => ({
16-
...member,
17-
permissions: member.permissions === '' ? [] : member.permissions.split(','),
18-
}));
17+
return members.map(member => mapValues(member, JSONParse));
1918
}
2019

2120
module.exports = getOrganizationMembers;

src/utils/setOrganizationMetadata.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ async function setOrganizationMetadata(opts) {
3232
}
3333

3434
metaOps.forEach((meta, idx) => handleAudience(pipe, keys[idx], meta));
35-
await pipe.exec().then(handlePipeline);
35+
return pipe.exec().then(handlePipeline);
3636
}
3737

3838
return true;

test/suites/organization/invites/accept.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ describe('#accept invite organization', function registerSuite() {
5151
});
5252
});
5353

54-
it('must return user not organization member error', async function test() {
54+
it('must return user not found error', async function test() {
5555
const acceptOpts = {
5656
organizationId: this.organization.id,
5757
username: faker.internet.email(),
@@ -63,7 +63,7 @@ describe('#accept invite organization', function registerSuite() {
6363
.then(inspectPromise(false))
6464
.then((response) => {
6565
assert.equal(response.name, 'HttpStatusError');
66-
assert.equal(response.message, 'username not member of organization');
66+
assert.ok(response.message.includes('does not exist'));
6767
});
6868
});
6969
});

0 commit comments

Comments
 (0)