Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions schemas/organization.members.get.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "object",
"required": [
"organizationId",
"username"
],
"properties": {
"organizationId": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
19 changes: 19 additions & 0 deletions schemas/organization.members.update.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"type": "object",
"required": [
"organizationId",
"username",
"data"
],
"properties": {
"organizationId": {
"type": "string"
},
"username": {
"type": "string"
},
"data": {
"$ref": "common.json#/definitions/metadata"
}
}
}
47 changes: 47 additions & 0 deletions src/actions/organization/members/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const { ActionTransport } = require('@microfleet/core');
const redisKey = require('../../../utils/key');
const getUserId = require('../../../utils/userData/get-user-id');
const { getMemberData } = require('../../../utils/organization/get-organization-members');
const { checkOrganizationExists } = require('../../../utils/organization');
const {
ORGANIZATIONS_MEMBERS,
USERS_METADATA,
ErrorUserNotMember,
} = require('../../../constants');

/**
* @api {amqp} <prefix>.members.Get Get organization member
* @apiVersion 1.0.0
* @apiName members.get
* @apiGroup Organizations
*
* @apiDescription This should be used to get member.
*
* @apiParam (Payload) {String} organizationId - organization id.
* @apiParam (Payload) {String} username - member email.
*/
async function getMember({ params }) {
const { redis, config } = this;
const { organizationId, username } = params;
const { audience } = config.organizations;

const userId = await getUserId.call(this, username);
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId);
const memberMetadataKey = redisKey(userId, USERS_METADATA, audience);
const userInOrganization = await redis.hget(memberMetadataKey, organizationId);
if (!userInOrganization) {
throw ErrorUserNotMember;
}

const member = await getMemberData.call(this, memberKey);

return {
data: {
attributes: member,
},
};
}

getMember.allowed = checkOrganizationExists;
getMember.transports = [ActionTransport.amqp, ActionTransport.internal];
module.exports = getMember;
51 changes: 51 additions & 0 deletions src/actions/organization/members/update.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const { ActionTransport } = require('@microfleet/core');
const redisKey = require('../../../utils/key');
const getUserId = require('../../../utils/userData/get-user-id');
const updateMember = require('../../../utils/organization/update-organization-members');
const { checkOrganizationExists } = require('../../../utils/organization');
const { getMemberData } = require('../../../utils/organization/get-organization-members');
const {
ORGANIZATIONS_MEMBERS,
USERS_METADATA,
ErrorUserNotMember,
} = require('../../../constants');

/**
* @api {amqp} <prefix>.members.update Update organization member
* @apiVersion 1.0.0
* @apiName members.update
* @apiGroup Organizations
*
* @apiDescription This should be used to update member.
*
* @apiParam (Payload) {String} organizationId - organization id.
* @apiParam (Payload) {String} username - member email.
* @apiParam (Payload) {Object[]} [data] - supports `$set key:value`, `$remove keys[]`
*/
async function updateMemberData({ params }) {
const { redis, config } = this;
const { organizationId, username, data } = params;
const { audience } = config.organizations;

const userId = await getUserId.call(this, username);
const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId);
const memberMetadataKey = redisKey(userId, USERS_METADATA, audience);
const userInOrganization = await redis.hget(memberMetadataKey, organizationId);

if (!userInOrganization) {
throw ErrorUserNotMember;
}

await updateMember.call(this, memberKey, data);
const member = await getMemberData.call(this, memberKey);

return {
data: {
attributes: member,
},
};
}

updateMemberData.allowed = checkOrganizationExists;
updateMemberData.transports = [ActionTransport.amqp, ActionTransport.internal];
module.exports = updateMemberData;
1 change: 1 addition & 0 deletions src/utils/organization/get-organization-members.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ async function getOrganizationMembers(organizationId) {
}

module.exports = getOrganizationMembers;
module.exports.getMemberData = getMemberData;
27 changes: 27 additions & 0 deletions src/utils/organization/update-organization-members.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* eslint-disable no-mixed-operators */
const mapValues = require('lodash/mapValues');
const handlePipeline = require('../pipeline-error');

const JSONStringify = (data) => JSON.stringify(data);

async function updateMember(memberKey, memberData) {
const { redis } = this;
const pipeline = redis.pipeline();

const { $remove } = memberData;
const $removeOps = $remove && $remove.length || 0;
if ($removeOps > 0) {
pipeline.hdel(memberKey, $remove);
}

const { $set } = memberData;
const $setKeys = $set && Object.keys($set);
const $setLength = $setKeys && $setKeys.length || 0;
if ($setLength > 0) {
pipeline.hmset(memberKey, mapValues($set, JSONStringify));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const values = $set ? Object.values($set) : false

if (values && values.length) {
  pipeline.hmset(memberKey, values.map(JSONStringify))
}

}

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

module.exports = updateMember;
34 changes: 34 additions & 0 deletions test/suites/actions/organization/members/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* eslint-disable promise/always-return, no-prototype-builtins */
const { inspectPromise } = require('@makeomatic/deploy');
const assert = require('assert');
const { createOrganization, createMembers } = require('../../../../helpers/organization');

describe('#get organization member', function registerSuite() {
this.timeout(50000);

beforeEach(global.startService);
beforeEach(function pretest() { return createMembers.call(this); });
beforeEach(function pretest() { return createOrganization.call(this); });
afterEach(global.clearRedis);

it('must reject invalid params and return detailed error', function test() {
return this.dispatch('users.organization.members.get', {})
.reflect()
.then(inspectPromise(false))
.then((response) => {
assert.equal(response.name, 'HttpStatusError');
assert.equal(response.errors.length, 2);
});
});

it('must be able to get member', async function test() {
const opts = {
organizationId: this.organization.id,
username: this.userNames[0].email,
};

await this.dispatch('users.organization.members.get', opts).then((response) => {
assert.deepEqual(response.data.attributes.username, opts.username);
});
});
});
39 changes: 39 additions & 0 deletions test/suites/actions/organization/members/update.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* eslint-disable promise/always-return, no-prototype-builtins */
const { inspectPromise } = require('@makeomatic/deploy');
const assert = require('assert');
const { createOrganization, createMembers } = require('../../../../helpers/organization');

describe('#update organization member', function registerSuite() {
this.timeout(50000);

beforeEach(global.startService);
beforeEach(function pretest() { return createMembers.call(this); });
beforeEach(function pretest() { return createOrganization.call(this); });
afterEach(global.clearRedis);

it('must reject invalid params and return detailed error', function test() {
return this.dispatch('users.organization.members.update', {})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

как-то так:

await assert.rejects(this.dispatch('users.organization.members.update', {}), {
   name: 'HttpStatusError'
})

.reflect()
.then(inspectPromise(false))
.then((response) => {
assert.equal(response.name, 'HttpStatusError');
assert.equal(response.errors.length, 3);
});
});

it('must be able to update member', async function test() {
const opts = {
organizationId: this.organization.id,
username: this.userNames[0].email,
data: {
$set: {
avatar: 'test-avatar',
},
},
};

await this.dispatch('users.organization.members.update', opts).then((response) => {
assert.deepEqual(response.data.attributes.avatar, 'test-avatar');
});
});
});
20 changes: 10 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4795,7 +4795,7 @@ growl@1.10.5:
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==

handlebars@^4.2.0, handlebars@^4.4.0, handlebars@^4.7.3, handlebars@^4.7.6:
handlebars@^4.4.0, handlebars@^4.7.3, handlebars@^4.7.6:
version "4.7.6"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e"
integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==
Expand Down Expand Up @@ -6783,13 +6783,13 @@ ms-mailer-client@^8.0.2:
lodash.merge "^4.6.2"

ms-mailer-templates@^1.17.0:
version "1.18.0"
resolved "https://registry.yarnpkg.com/ms-mailer-templates/-/ms-mailer-templates-1.18.0.tgz#324ec20bb3e09d52d24e1170c9a4f5872d054696"
integrity sha512-3sp7H+nTRegWTA8ntV+ZnizzJfS9U/exXedT62JITaOmFuzPA1I+f2d8cnH0Cn0zRXoEaszuej8PZerAp2fvTg==
version "1.19.1"
resolved "https://registry.yarnpkg.com/ms-mailer-templates/-/ms-mailer-templates-1.19.1.tgz#a38c21dd1a652aed5b0f0fc9c209f8ec7317a879"
integrity sha512-0/w8aoSqAtSVk1xkOIpewerglz6G9Wt9MC369Pz/QZpHrTm4cqgyVBZC3JoYvR4jIxiEIhRoxVl4okoeqXEAZw==
dependencies:
bluebird "^3.5.5"
common-errors "^1.0.5"
handlebars "^4.2.0"
bluebird "^3.7.2"
common-errors "^1.2.0"
handlebars "^4.7.6"

ms-token@^5.0.1:
version "5.0.1"
Expand Down Expand Up @@ -9702,9 +9702,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==

uglify-js@^3.1.4:
version "3.11.6"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.11.6.tgz#144b50d3e05eadd3ad4dd047c60ca541a8cd4e9c"
integrity sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g==
version "3.12.0"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.12.0.tgz#b943f129275c41d435eb54b643bbffee71dccf57"
integrity sha512-8lBMSkFZuAK7gGF8LswsXmir8eX8d2AAMOnxSDWjKBx/fBR6MypQjs78m6ML9zQVp1/hD4TBdfeMZMC7nW1TAA==

uid-number@0.0.6:
version "0.0.6"
Expand Down