diff --git a/Dockerfile b/Dockerfile index 433cacbb..e456dd4d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,10 +9,7 @@ USER node:node WORKDIR /src COPY --chown=node:node . . -# Workaround https://github.com/CyCoreSystems/go-meteor/issues/6 -RUN meteor npm install --save postcss-easy-import - -RUN meteor npm install --production +RUN meteor npm install RUN meteor build --architecture os.linux.x86_64 --directory /bundle RUN cd /bundle/bundle/programs/server && npm install diff --git a/imports/api/rest/put-pending-invitations.js b/imports/api/rest/put-pending-invitations.js index 9ff948b2..1aca4805 100644 --- a/imports/api/rest/put-pending-invitations.js +++ b/imports/api/rest/put-pending-invitations.js @@ -3,9 +3,8 @@ import { invite } from '../../util/email-invite' import { Meteor } from 'meteor/meteor' import { reloadCaseFields } from '../cases' -import UnitRolesData from '../unit-roles-data' +import UnitRolesData, { defaultRoleVisibility } from '../unit-roles-data' import { logger } from '../../util/logger' -import { defaultRoleVisibility } from '../units' export default (req, res) => { if (req.query.accessToken !== process.env.API_ACCESS_TOKEN) { diff --git a/imports/api/unit-roles-data.js b/imports/api/unit-roles-data.js index 6d1abe6e..480b30f7 100644 --- a/imports/api/unit-roles-data.js +++ b/imports/api/unit-roles-data.js @@ -6,10 +6,14 @@ import { check } from 'meteor/check' import { HTTP } from 'meteor/http' import { Random } from 'meteor/random' import randToken from 'rand-token' -import { addUserToRole, defaultRoleVisibility } from './units' import { findOrCreateUser } from './custom-users' import UnitMetaData from './unit-meta-data' -import PendingInvitations, { KEEP_DEFAULT, REMOVE_USER, REPLACE_DEFAULT, collectionName as pendingInvitaitonsCollName } from './pending-invitations' +import PendingInvitations, { + KEEP_DEFAULT, + REMOVE_USER, + REPLACE_DEFAULT, + collectionName as pendingInvitationsCollName +} from './pending-invitations' import unitUserInvitedTemplate from '../email-templates/unit-user-invited' import { logger } from '../util/logger' import { getIncrementFor } from './increment-counters' @@ -42,6 +46,23 @@ export const possibleRoles = [ } ] +export const defaultRoleVisibility = { + [roleEnum.TENANT]: true, + [roleEnum.OWNER_LANDLORD]: true, + [roleEnum.CONTRACTOR]: true, + [roleEnum.MGT_COMPANY]: true, + [roleEnum.AGENT]: true, + 'Occupant': true +} + +export const roleSortOrder = [ + roleEnum.TENANT, + roleEnum.OWNER_LANDLORD, + roleEnum.AGENT, + roleEnum.MGT_COMPANY, + roleEnum.CONTRACTOR +] + const UnitRolesData = new Mongo.Collection(collectionName) const roleDocMemberMatcher = memberId => roleDoc => roleDoc.members.find(member => member.id === memberId) @@ -77,6 +98,115 @@ export function inviteUserToRole (invitorId, unitMongoId, inviteeUser, roleType, return { accessToken } } +export const addUserToRole = ( + invitingUser, inviteeUser, unitBzId, role, invType, isOccupant, errorLogParams = {}, doLiveUpdate, isVisible = true, + isDefaultInvited = true, roleVisibility = defaultRoleVisibility +) => { + // Filling up role visibility in case some of it is missing + roleVisibility = Object.assign({}, defaultRoleVisibility, roleVisibility) + + // Creating matching invitation records + const invitationObj = { + invitedBy: invitingUser.bugzillaCreds.id, + invitee: inviteeUser.bugzillaCreds.id, + mefeInvitationIdIntValue: getIncrementFor(pendingInvitationsCollName), + type: invType, + unitId: unitBzId, + role, + isOccupant + } + + // TODO: Once all dependencies of role resolving are moved from invitations to UnitRolesData, remove this + // Creating the invitation as pending first + const invitationId = PendingInvitations.insert(invitationObj) + + // Linking invitation to user + Meteor.users.update(inviteeUser._id, { + $push: { + receivedInvites: { + unitId: invitationObj.unitId, + invitedBy: invitingUser._id, + timestamp: Date.now(), + type: invitationObj.type, + invitationId, + role, + isOccupant + } + } + }) + + // Adding to the user to a role on BZ using lambda + try { + HTTP.call('POST', process.env.INVITE_LAMBDA_URL, { + data: [Object.assign({ _id: invitationId }, invitationObj)], + headers: { + Authorization: `Bearer ${process.env.API_ACCESS_TOKEN}` + } + }) + } catch (e) { + logger.error({ + ...errorLogParams, + step: 'INVITE lambda request, unit cleanup might be necessary', + error: e + }) + throw new Meteor.Error('Invite API Lambda error', e, { lambdaStatusCode: e.response.statusCode }) + } + + // Marking the pending invitation as "done", now that the API responded with success + PendingInvitations.update({ _id: invitationId }, { + $set: { + done: true + } + }) + Meteor.users.update({ + _id: inviteeUser._id, + 'receivedInvites.invitationId': invitationId + }, { + $set: { + 'receivedInvites.$.done': true + } + }) + + // Updating the roles collection to sync with BZ's state + const unitRoleQuery = { + roleType: role, + unitBzId + } + + const unitRoleModifier = { + $push: { + members: { + id: inviteeUser._id, + isVisible, + isDefaultInvited, + isOccupant, + roleVisibility + } + } + } + + UnitRolesData.update(unitRoleQuery, unitRoleModifier) + + // Matching the role if the defaultAssigneeId is not defined and sets it to the current user. Does nothing otherwise + let doForceAssigneeUpdate + switch (invType) { + case REPLACE_DEFAULT: + doForceAssigneeUpdate = true + break + default: + doForceAssigneeUpdate = false + } + const assigneeUpdateQuery = doForceAssigneeUpdate ? unitRoleQuery : { + defaultAssigneeId: -1, + ...unitRoleQuery + } + UnitRolesData.update(assigneeUpdateQuery, { + $set: { + defaultAssigneeId: inviteeUser._id + } + }) +} + export function removeRoleMember (requestorId, unitBzId, email, errorLogParams) { const unitMeta = UnitMetaData.findOne({ bzId: unitBzId }) const unitRoles = UnitRolesData.find({ unitBzId }).fetch() @@ -102,7 +232,7 @@ export function removeRoleMember (requestorId, unitBzId, email, errorLogParams) const invitationObj = { invitedBy: requestorUser.bugzillaCreds.id, invitee: userToRemove.bugzillaCreds.id, - mefeInvitationIdIntValue: getIncrementFor(pendingInvitaitonsCollName), + mefeInvitationIdIntValue: getIncrementFor(pendingInvitationsCollName), type: REMOVE_USER, unitId: unitBzId, role: toRemoveRole.roleType, diff --git a/imports/api/units.js b/imports/api/units.js index 766458c5..2a14b23f 100644 --- a/imports/api/units.js +++ b/imports/api/units.js @@ -7,8 +7,13 @@ import _ from 'lodash' import publicationFactory from './base/rest-resource-factory' import { makeAssociationFactory, withUsers, withDocs } from './base/associations-helper' import UnitMetaData, { unitTypes, collectionName as unitMetaCollName } from './unit-meta-data' -import UnitRolesData, { possibleRoles, roleEnum, collectionName as unitRolesCollName } from './unit-roles-data' -import PendingInvitations, { REPLACE_DEFAULT, collectionName as pendingInvitationsCollName } from './pending-invitations' +import UnitRolesData, { + possibleRoles, + roleSortOrder, + addUserToRole, + collectionName as unitRolesCollName +} from './unit-roles-data' +import { REPLACE_DEFAULT } from './pending-invitations' import { callAPI } from '../util/bugzilla-api' import { logger } from '../util/logger' import FailedUnitCreations from './failed-unit-creations' @@ -25,23 +30,6 @@ export const factoryOptions = { export let serverHelpers -export const defaultRoleVisibility = { - [roleEnum.TENANT]: true, - [roleEnum.OWNER_LANDLORD]: true, - [roleEnum.CONTRACTOR]: true, - [roleEnum.MGT_COMPANY]: true, - [roleEnum.AGENT]: true, - 'Occupant': true -} - -const roleSortOrder = [ - roleEnum.TENANT, - roleEnum.OWNER_LANDLORD, - roleEnum.AGENT, - roleEnum.MGT_COMPANY, - roleEnum.CONTRACTOR -] - if (Meteor.isServer) { serverHelpers = { getAPIUnitByName (unitName, apiKey) { @@ -192,129 +180,6 @@ export const getUnitRoles = (unit, userId) => { ) } -export const addUserToRole = ( - invitingUser, inviteeUser, unitBzId, role, invType, isOccupant, errorLogParams = {}, doLiveUpdate, isVisible = true, - isDefaultInvited = true, roleVisibility = defaultRoleVisibility -) => { - // Filling up role visibility in case some of it is missing - roleVisibility = Object.assign({}, defaultRoleVisibility, roleVisibility) - - // Creating matching invitation records - const invitationObj = { - invitedBy: invitingUser.bugzillaCreds.id, - invitee: inviteeUser.bugzillaCreds.id, - mefeInvitationIdIntValue: getIncrementFor(pendingInvitationsCollName), - type: invType, - unitId: unitBzId, - role, - isOccupant - } - - // TODO: Once all dependencies of role resolving are moved from invitations to UnitRolesData, remove this - // Creating the invitation as pending first - const invitationId = PendingInvitations.insert(invitationObj) - - // Linking invitation to user - Meteor.users.update(inviteeUser._id, { - $push: { - receivedInvites: { - unitId: invitationObj.unitId, - invitedBy: invitingUser._id, - timestamp: Date.now(), - type: invitationObj.type, - invitationId, - role, - isOccupant - } - } - }) - - // Adding to the user to a role on BZ using lambda - try { - HTTP.call('POST', process.env.INVITE_LAMBDA_URL, { - data: [Object.assign({ _id: invitationId }, invitationObj)], - headers: { - Authorization: `Bearer ${process.env.API_ACCESS_TOKEN}` - } - }) - } catch (e) { - logger.error({ - ...errorLogParams, - step: 'INVITE lambda request, unit cleanup might be necessary', - error: e - }) - throw new Meteor.Error('Invite API Lambda error', e, { lambdaStatusCode: e.response.statusCode }) - } - - // Marking the pending invitation as "done", now that the API responded with success - PendingInvitations.update({ _id: invitationId }, { - $set: { - done: true - } - }) - Meteor.users.update({ - _id: inviteeUser._id, - 'receivedInvites.invitationId': invitationId - }, { - $set: { - 'receivedInvites.$.done': true - } - }) - - // Updating the roles collection to sync with BZ's state - const unitRoleQuery = { - roleType: role, - unitBzId - } - - const unitRoleModifier = { - $push: { - members: { - id: inviteeUser._id, - isVisible, - isDefaultInvited, - isOccupant, - roleVisibility - } - } - } - - UnitRolesData.update(unitRoleQuery, unitRoleModifier) - - // Matching the role if the defaultAssigneeId is not defined and sets it to the current user. Does nothing otherwise - let doForceAssigneeUpdate - switch (invType) { - case REPLACE_DEFAULT: - doForceAssigneeUpdate = true - break - default: - doForceAssigneeUpdate = false - } - const assigneeUpdateQuery = doForceAssigneeUpdate ? unitRoleQuery : { - defaultAssigneeId: -1, - ...unitRoleQuery - } - UnitRolesData.update(assigneeUpdateQuery, { - $set: { - defaultAssigneeId: inviteeUser._id - } - }) - - if (doLiveUpdate) { - try { - const response = callAPI('get', `/rest/product?ids=${unitBzId}`, {}, true, true) - const unitItem = factoryOptions.dataResolver(response.data)[0] - pubObj.handleChanged(unitItem, ['components']) - } catch (e) { - logger.error({ - ...errorLogParams, - step: 'Fetching unit info for live update after invite, proceeding with no error', - error: e - }) - } - } -} - const rolesProjByOwnership = (userId, unitItem) => { return { unitId: 1, diff --git a/imports/migrations/13_add_role_visibility_to_all_members.js b/imports/migrations/13_add_role_visibility_to_all_members.js index a85da2b0..3599139c 100644 --- a/imports/migrations/13_add_role_visibility_to_all_members.js +++ b/imports/migrations/13_add_role_visibility_to_all_members.js @@ -1,6 +1,5 @@ import { Migrations } from 'meteor/percolate:migrations' -import UnitRolesData from '../api/unit-roles-data' -import { defaultRoleVisibility } from '../api/units' +import UnitRolesData, { defaultRoleVisibility } from '../api/unit-roles-data' Migrations.add({ version: 13, diff --git a/package-lock.json b/package-lock.json index fb46cdb3..b9553b1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -204,9 +204,9 @@ } }, "agent-base": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", - "integrity": "sha1-2J5ZmfeXh1Z0wH2H8mD8Qeg+jKk=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "dev": true, "requires": { "es6-promisify": "^5.0.0" @@ -329,9 +329,9 @@ "dev": true }, "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, "asynckit": { @@ -535,9 +535,9 @@ } }, "buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha1-h/yqOimDWOCt5uRCz86EB0DRrQQ=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "builtin-modules": { @@ -2021,23 +2021,29 @@ } }, "https-proxy-agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", - "integrity": "sha1-UVUpcPoE1yPgTFbQQXjD+SWSu8A=", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", + "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", "dev": true, "requires": { - "agent-base": "^4.1.0", + "agent-base": "^4.3.0", "debug": "^3.1.0" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true } } }, @@ -3247,9 +3253,9 @@ } }, "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha1-sWIcVNY7l8R9PP5/chX31kUXw2k=", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "dev": true }, "mime-db": { @@ -4077,19 +4083,19 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "puppeteer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.6.0.tgz", - "integrity": "sha512-88epdIp3lw0LxI+sIHgdgZdq/u5zRnzgU2vJGvcyuGqHQrtRUeICTexTyT1KoKhTGG0mAKFRV9c7IJ179agm7A==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz", + "integrity": "sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ==", "dev": true, "requires": { - "debug": "^3.1.0", + "debug": "^4.1.0", "extract-zip": "^1.6.6", "https-proxy-agent": "^2.2.1", "mime": "^2.0.3", - "progress": "^2.0.0", + "progress": "^2.0.1", "proxy-from-env": "^1.0.0", "rimraf": "^2.6.1", - "ws": "^5.1.1" + "ws": "^6.1.0" }, "dependencies": { "concat-stream": { @@ -4105,12 +4111,12 @@ } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "extract-zip": { @@ -4133,13 +4139,25 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true } } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true } } @@ -5702,9 +5720,9 @@ } }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha1-3/7xSGa46NyRM1glFNG++vlumA8=", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", "dev": true, "requires": { "async-limiter": "~1.0.0" diff --git a/package.json b/package.json index 6a742069..91317c63 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "postcss-modules-local-by-default": "^1.2.0", "postcss-modules-scope": "^1.1.0", "postcss-nested": "^1.0.1", - "puppeteer": "^1.6.0", + "puppeteer": "^1.20.0", "react-addons-test-utils": "^15.6.0", "react-test-renderer": "^15.6.1", "snazzy": "^7.0.0",