Skip to content

Commit aba2e92

Browse files
authored
Merge pull request #163 from developmentseed/feature/organizations-api
Feature/organizations api
2 parents 10ce687 + 3b62c0b commit aba2e92

21 files changed

+894
-219
lines changed

app/lib/organization.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ async function createOrgTeam (organizationId, data, osmId) {
165165
return conn.transaction(async trx => {
166166
const record = await team.create(data, osmId, trx)
167167
await trx('organization_team').insert({ team_id: record.id, organization_id: organizationId })
168+
return record
168169
})
169170
}
170171

app/lib/team.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,20 @@ async function isPublic (teamId) {
332332
return (privacy === 'public')
333333
}
334334

335+
/**
336+
* associatedOrg
337+
*
338+
* If the team is part of an org, return its associated org
339+
* @param teamId - team id
340+
* @returns {int} organization id or null
341+
*/
342+
async function associatedOrg (teamId) {
343+
if (!teamId) throw new Error('team id is required as first argument')
344+
345+
const conn = await db()
346+
return unpack(conn('organization_team').where('team_id', teamId).returning('organization_id'))
347+
}
348+
335349
module.exports = {
336350
get,
337351
list,
@@ -349,5 +363,6 @@ module.exports = {
349363
isModerator,
350364
isMember,
351365
isPublic,
352-
resolveMemberNames
366+
resolveMemberNames,
367+
associatedOrg
353368
}

app/manage/index.js

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ const {
2121
updateTeam
2222
} = require('./teams')
2323

24+
const {
25+
createOrg,
26+
getOrg,
27+
updateOrg,
28+
destroyOrg,
29+
addOwner,
30+
removeOwner,
31+
addManager,
32+
removeManager,
33+
createOrgTeam,
34+
getOrgTeams
35+
} = require('./organizations')
36+
2437
/**
2538
* The manageRouter handles all routes related to the first party
2639
* management client
@@ -62,16 +75,33 @@ function manageRouter (nextApp) {
6275
*/
6376
router.get('/api/teams', listTeams)
6477
router.get('/api/my/teams', can('public:authenticated'), listMyTeams)
65-
router.post('/api/teams', can('team:create'), createTeam)
78+
router.post('/api/teams', can('public:authenticated'), createTeam)
6679
router.get('/api/teams/:id', can('team:view'), getTeam)
67-
router.put('/api/teams/:id', can('team:update'), updateTeam)
68-
router.delete('/api/teams/:id', can('team:delete'), destroyTeam)
69-
router.put('/api/teams/add/:id/:osmId', can('team:update'), addMember)
70-
router.put('/api/teams/remove/:id/:osmId', can('team:update'), removeMember)
71-
router.patch('/api/teams/:id/members', can('team:update'), updateMembers)
80+
router.put('/api/teams/:id', can('team:edit'), updateTeam)
81+
router.delete('/api/teams/:id', can('team:edit'), destroyTeam)
82+
router.put('/api/teams/add/:id/:osmId', can('team:edit'), addMember)
83+
router.put('/api/teams/remove/:id/:osmId', can('team:edit'), removeMember)
84+
router.patch('/api/teams/:id/members', can('team:edit'), updateMembers)
7285
router.put('/api/teams/:id/join', can('team:join'), joinTeam)
73-
router.put('/api/teams/:id/assignModerator/:osmId', can('team:update'), assignModerator)
74-
router.put('/api/teams/:id/removeModerator/:osmId', can('team:update'), removeModerator)
86+
router.put('/api/teams/:id/assignModerator/:osmId', can('team:edit'), assignModerator)
87+
router.put('/api/teams/:id/removeModerator/:osmId', can('team:edit'), removeModerator)
88+
89+
/**
90+
* List, Create, Read, Update, Delete operations on orgs
91+
*/
92+
router.post('/api/organizations', can('public:authenticated'), createOrg)
93+
router.get('/api/organizations/:id', can('public:authenticated'), getOrg) // TODO handle private organizations
94+
router.put('/api/organizations/:id', can('organization:edit'), updateOrg)
95+
router.delete('/api/organizations/:id', can('organization:edit'), destroyOrg)
96+
97+
router.put('/api/organizations/:id/addOwner/:osmId', can('organization:edit'), addOwner)
98+
router.put('/api/organizations/:id/removeOwner/:osmId', can('organization:edit'), removeOwner)
99+
100+
router.put('/api/organizations/:id/addManager/:osmId', can('organization:edit'), addManager)
101+
router.put('/api/organizations/:id/removeManager/:osmId', can('organization:edit'), removeManager)
102+
103+
router.post('/api/organizations/:id/teams', can('organization:create-team'), createOrgTeam)
104+
router.get('/api/organizations/:id/teams', getOrgTeams)
75105

76106
/**
77107
* Page renders

app/manage/organizations.js

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
const organization = require('../lib/organization')
2+
const team = require('../lib/team')
3+
4+
/**
5+
* Create an organization
6+
* Uses the user id in the request and the body to forward
7+
* to the organization model
8+
*/
9+
async function createOrg (req, reply) {
10+
const { body } = req
11+
const { user_id } = reply.locals
12+
13+
try {
14+
const data = await organization.create(body, user_id)
15+
reply.send(data)
16+
} catch (err) {
17+
console.log(err)
18+
return reply.boom.badRequest(err.message)
19+
}
20+
}
21+
22+
/**
23+
* Get an organization
24+
* Requires id of organization
25+
*/
26+
async function getOrg (req, reply) {
27+
const { id } = req.params
28+
29+
if (!id) {
30+
return reply.boom.badRequest('organization id is required')
31+
}
32+
33+
try {
34+
const [data, owners, managers] = await Promise.all([
35+
organization.get(id),
36+
organization.getOwners(id),
37+
organization.getManagers(id)
38+
])
39+
40+
reply.send({ ...data, owners, managers })
41+
} catch (err) {
42+
console.log(err)
43+
return reply.boom.badRequest(err.message)
44+
}
45+
}
46+
47+
/**
48+
* Update an organization
49+
* Requires the id of the organization to modify
50+
*/
51+
async function updateOrg (req, reply) {
52+
const { id } = req.params
53+
const { body } = req
54+
55+
if (!id) {
56+
return reply.boom.badRequest('organization id is required')
57+
}
58+
59+
try {
60+
const data = await organization.update(id, body)
61+
reply.send(data)
62+
} catch (err) {
63+
console.log(err)
64+
return reply.boom.badRequest(err.message)
65+
}
66+
}
67+
68+
/**
69+
* Destroy an organization
70+
*/
71+
async function destroyOrg (req, reply) {
72+
const { id } = req.params
73+
74+
if (!id) {
75+
return reply.boom.badRequest('organization id is required')
76+
}
77+
78+
try {
79+
await organization.destroy(id)
80+
reply.sendStatus(200)
81+
} catch (err) {
82+
console.log(err)
83+
return reply.boom.badRequest(err.message)
84+
}
85+
}
86+
87+
/**
88+
* Add owner
89+
*/
90+
async function addOwner (req, reply) {
91+
const { id, osmId } = req.params
92+
93+
if (!id) {
94+
return reply.boom.badRequest('organization id is required')
95+
}
96+
97+
if (!osmId) {
98+
return reply.boom.badRequest('osmId to add is required')
99+
}
100+
101+
try {
102+
await organization.addOwner(id, Number(osmId))
103+
reply.sendStatus(200)
104+
} catch (err) {
105+
console.log(err)
106+
return reply.boom.badRequest(err.message)
107+
}
108+
}
109+
110+
/**
111+
* Remove owner
112+
*/
113+
async function removeOwner (req, reply) {
114+
const { id, osmId } = req.params
115+
116+
if (!id) {
117+
return reply.boom.badRequest('organization id is required')
118+
}
119+
120+
if (!osmId) {
121+
return reply.boom.badRequest('osmId to add is required')
122+
}
123+
124+
try {
125+
await organization.removeOwner(id, Number(osmId))
126+
reply.sendStatus(200)
127+
} catch (err) {
128+
console.log(err)
129+
return reply.boom.badRequest(err.message)
130+
}
131+
}
132+
133+
/**
134+
* Add manager
135+
*/
136+
async function addManager (req, reply) {
137+
const { id, osmId } = req.params
138+
139+
if (!id) {
140+
return reply.boom.badRequest('organization id is required')
141+
}
142+
143+
if (!osmId) {
144+
return reply.boom.badRequest('osmId to add is required')
145+
}
146+
147+
try {
148+
await organization.addManager(id, Number(osmId))
149+
reply.sendStatus(200)
150+
} catch (err) {
151+
console.log(err)
152+
return reply.boom.badRequest(err.message)
153+
}
154+
}
155+
156+
/**
157+
* Remove manager
158+
*/
159+
async function removeManager (req, reply) {
160+
const { id, osmId } = req.params
161+
162+
if (!id) {
163+
return reply.boom.badRequest('organization id is required')
164+
}
165+
166+
if (!osmId) {
167+
return reply.boom.badRequest('osmId to add is required')
168+
}
169+
170+
try {
171+
await organization.removeManager(id, Number(osmId))
172+
reply.sendStatus(200)
173+
} catch (err) {
174+
console.log(err)
175+
return reply.boom.badRequest(err.message)
176+
}
177+
}
178+
179+
/**
180+
* Create org team
181+
*/
182+
async function createOrgTeam (req, reply) {
183+
const { id } = req.params
184+
const { body } = req
185+
const { user_id } = reply.locals
186+
187+
try {
188+
const data = await organization.createOrgTeam(id, body, user_id)
189+
reply.send(data)
190+
} catch (err) {
191+
console.log(err)
192+
return reply.boom.badRequest(err.message)
193+
}
194+
}
195+
196+
/**
197+
* List org teams
198+
*/
199+
async function getOrgTeams (req, reply) {
200+
const { id } = req.params
201+
try {
202+
const data = await team.list({ organizationId: id })
203+
reply.send(data)
204+
} catch (err) {
205+
console.log(err)
206+
return reply.boom.badRequest(err.message)
207+
}
208+
}
209+
210+
module.exports = {
211+
createOrg,
212+
getOrg,
213+
updateOrg,
214+
destroyOrg,
215+
addOwner,
216+
removeOwner,
217+
addManager,
218+
removeManager,
219+
createOrgTeam,
220+
getOrgTeams
221+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const { isOwner, isManager } = require('../../lib/organization')
2+
3+
/**
4+
* organization:create-team
5+
*
6+
* To create a team within an organization, you have to be
7+
* a manager of the organization
8+
*
9+
* @param {int} uid - user id
10+
* @param {Object} params - request parameters
11+
* @param {int} params.id - organization id
12+
* @returns {Promise<boolean>}
13+
*/
14+
async function createOrgTeam (uid, { id }) {
15+
return (await isOwner(id, uid)) || isManager(id, uid)
16+
}
17+
18+
module.exports = createOrgTeam

app/manage/permissions/create-team.js

Lines changed: 0 additions & 22 deletions
This file was deleted.

app/manage/permissions/delete-team.js

Lines changed: 0 additions & 17 deletions
This file was deleted.

app/manage/permissions/edit-org.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const { isOwner } = require('../../lib/organization')
2+
3+
/**
4+
* organization:edit
5+
*
6+
* To edit an organization or delete it, the authenticated user needs
7+
* to be an owner in the organization
8+
*
9+
* @param {int} uid - user id
10+
* @param {Object} params - request parameters
11+
* @param {int} params.id - organization id
12+
* @returns {Promise<boolean>}
13+
*/
14+
async function editOrg (uid, { id }) {
15+
return isOwner(id, uid)
16+
}
17+
18+
module.exports = editOrg

0 commit comments

Comments
 (0)