Skip to content

Commit 772bd17

Browse files
committed
workspace invites
1 parent d18f17f commit 772bd17

8 files changed

+133
-12
lines changed

netlify/functions/getWorkspaceTeam.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
const extrovert = require('extrovert');
4+
5+
module.exports = extrovert.toNetlifyFunction(require('../../src/actions/getWorkspaceTeam'));

src/actions/getWorkspace.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
const Archetype = require('archetype');
44
const connect = require('../../src/db');
5-
const extrovert = require('extrovert');
65

76
const GetWorkspaceParams = new Archetype({
87
apiKey: {
@@ -18,5 +17,6 @@ module.exports = async function getWorkspace(params) {
1817
const { Workspace } = db.models;
1918

2019
const workspace = await Workspace.findOne({ apiKey }).orFail();
20+
2121
return { workspace };
2222
};

src/actions/getWorkspaceTeam.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
const Archetype = require('archetype');
4+
const connect = require('../../src/db');
5+
const mongoose = require('mongoose');
6+
7+
const GetInvitationsForWorkspaceParams = new Archetype({
8+
authorization: {
9+
$type: 'string',
10+
$required: true
11+
},
12+
workspaceId: {
13+
$type: mongoose.Types.ObjectId,
14+
$required: true
15+
}
16+
}).compile('GetInvitationsParams');
17+
18+
module.exports = async function getWorkspaceTeam(params) {
19+
const { authorization, workspaceId } = new GetInvitationsForWorkspaceParams(params);
20+
21+
const db = await connect();
22+
const { AccessToken, Invitation, User, Workspace } = db.models;
23+
24+
// Find the user linked to the access token
25+
const accessToken = await AccessToken.findById(authorization).orFail(new Error('Invalid access token'));
26+
const userId = accessToken.userId;
27+
28+
// Find the workspace and check user permissions
29+
const workspace = await Workspace.findById(workspaceId).orFail(new Error('Workspace not found'));
30+
31+
const isAuthorized = workspace.members.some(member =>
32+
member.userId.toString() === userId.toString() && member.roles.find(role => role === 'admin' || role === 'owner')
33+
);
34+
35+
if (!isAuthorized) {
36+
throw new Error('User is not authorized to view workspace team');
37+
}
38+
39+
// Fetch invitations with status "pending" or "declined"
40+
const invitations = await Invitation.find({
41+
workspaceId,
42+
status: { $in: ['pending', 'declined'] }
43+
});
44+
45+
const users = await User.find({ _id: { $in: workspace.members.map(member => member.userId) } });
46+
47+
return { invitations, users, workspace };
48+
};

src/actions/inviteToWorkspace.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ module.exports = async function inviteToWorkspace(params) {
4848

4949
const isAlreadyMember = await User.exists(
5050
{ _id: { $in: workspace.members.map(member => member.userId) },
51-
githubUsername
52-
});
51+
githubUsername
52+
});
5353
if (isAlreadyMember) {
5454
throw new Error(`${githubUsername} is already a member of this workspace`);
5555
}

src/db/invitation.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ const invitationSchema = new mongoose.Schema({
1313
type: String,
1414
required: true
1515
},
16+
email: {
17+
type: String
18+
},
1619
invitedBy: {
1720
type: mongoose.Schema.Types.ObjectId,
1821
ref: 'User',

src/db/user.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ const userSchema = new mongoose.Schema({
1919
},
2020
githubUserId: {
2121
type: String
22+
},
23+
isFreeUser: {
24+
type: Boolean
2225
}
2326
}, { timestamps: true, id: false });
2427

test/getWorkspaceTeam.test.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use strict';
2+
3+
const assert = require('assert');
4+
const { beforeEach, describe, it } = require('mocha');
5+
const connect = require('../src/db');
6+
const getWorkspaceTeam = require('../src/actions/getWorkspaceTeam');
7+
8+
describe('getWorkspaceTeam', function() {
9+
let db, User, AccessToken, Workspace, Invitation;
10+
let adminUser, adminAccessToken, workspace, invitation1, invitation2;
11+
12+
beforeEach(async function() {
13+
db = await connect();
14+
({ User, AccessToken, Workspace, Invitation } = db.models);
15+
16+
// Create an admin user
17+
adminUser = await User.create({ email: '[email protected]', githubUsername: 'admin' });
18+
19+
// Create an access token for the admin
20+
adminAccessToken = await AccessToken.create({ userId: adminUser._id });
21+
22+
// Create a workspace with the admin user
23+
workspace = await Workspace.create({
24+
name: 'Test Workspace',
25+
ownerId: adminUser._id,
26+
members: [{ userId: adminUser._id, roles: ['owner'] }],
27+
baseUrl: 'https://example.com',
28+
subscriptionTier: 'pro'
29+
});
30+
31+
// Create invitations with "pending" and "declined" statuses
32+
invitation1 = await Invitation.create({
33+
workspaceId: workspace._id,
34+
githubUsername: 'user1',
35+
status: 'pending',
36+
invitedBy: adminUser._id,
37+
roles: ['member']
38+
});
39+
40+
invitation2 = await Invitation.create({
41+
workspaceId: workspace._id,
42+
githubUsername: 'user2',
43+
status: 'declined',
44+
invitedBy: adminUser._id,
45+
roles: ['admin']
46+
});
47+
});
48+
49+
it('should return pending and declined invitations if user is an admin or owner', async function() {
50+
const result = await getWorkspaceTeam({
51+
authorization: adminAccessToken._id.toString(),
52+
workspaceId: workspace._id
53+
});
54+
55+
assert.strictEqual(result.invitations.length, 2);
56+
assert.strictEqual(result.invitations[0].workspaceId.toString(), workspace._id.toString());
57+
assert.strictEqual(result.invitations[1].workspaceId.toString(), workspace._id.toString());
58+
assert.deepStrictEqual(
59+
result.invitations.map(invite => invite.status).sort(),
60+
['declined', 'pending']
61+
);
62+
});
63+
});

test/inviteToWorkspace.test.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
'use strict';
22

3+
const { afterEach, beforeEach, describe, it } = require('mocha');
34
const assert = require('assert');
4-
const mongoose = require('mongoose');
5-
const inviteToWorkspace = require('../src/actions/inviteToWorkspace');
65
const connect = require('../src/db');
6+
const inviteToWorkspace = require('../src/actions/inviteToWorkspace');
77

8-
describe('inviteToWorkspace', function () {
8+
describe('inviteToWorkspace', function() {
99
let db, AccessToken, User, Workspace, Invitation;
1010
let user, workspace, accessToken;
1111

12-
beforeEach(async function () {
12+
beforeEach(async function() {
1313
// Connect to the real database
1414
db = await connect();
1515
({ AccessToken, User, Workspace, Invitation } = db.models);
@@ -41,15 +41,15 @@ describe('inviteToWorkspace', function () {
4141
});
4242
});
4343

44-
afterEach(async function () {
44+
afterEach(async function() {
4545
// Cleanup after tests
4646
await AccessToken.deleteMany({});
4747
await User.deleteMany({});
4848
await Workspace.deleteMany({});
4949
await Invitation.deleteMany({});
5050
});
5151

52-
it('should invite a user successfully', async function () {
52+
it('should invite a user successfully', async function() {
5353
const result = await inviteToWorkspace({
5454
authorization: accessToken._id.toString(),
5555
workspaceId: workspace._id,
@@ -67,8 +67,8 @@ describe('inviteToWorkspace', function () {
6767
assert.strictEqual(invitationInDb.workspaceId.toString(), workspace._id.toString());
6868
});
6969

70-
71-
it('should fail if user is already a member of the workspace', async function () {
70+
71+
it('should fail if user is already a member of the workspace', async function() {
7272
const invitedUser = await User.create({
7373
name: 'Jane Smith',
7474
@@ -91,4 +91,3 @@ describe('inviteToWorkspace', function () {
9191
);
9292
});
9393
});
94-

0 commit comments

Comments
 (0)