Skip to content

Commit 0205ea3

Browse files
author
Alex G Rice
authored
Add member and moderator ids to listTeams response. (#173)
* Add member and moderator ids to listTeams response. * Upgrade ava to latest. * JsDoc improvement. * Re-use ramda prop(). * Unit test for team.listMembers() and team.listModerators(). * Add moderator ids and member ids to getOrgTeams(). * Update OpenAPI spec. * Unit tests in organization-api and team-api.
1 parent 4c16665 commit 0205ea3

File tree

10 files changed

+1069
-638
lines changed

10 files changed

+1069
-638
lines changed

app/lib/team.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,34 @@ async function list (options) {
112112
return query
113113
}
114114

115+
/**
116+
* List the member osm ids for a list of team ids.
117+
*
118+
* @param {number[]} teamIds
119+
* @returns {Promise<*>}
120+
* @async
121+
*/
122+
async function listMembers (teamIds) {
123+
const conn = await db()
124+
return conn('member').whereIn('team_id', teamIds)
125+
.join('users', 'member.osm_id', 'users.id')
126+
.select('team_id', 'osm_id')
127+
}
128+
129+
/**
130+
* List the moderator osm ids for a list of team ids.
131+
*
132+
* @param {number[]} teamIds
133+
* @returns {Promise<*>}
134+
* @async
135+
*/
136+
async function listModerators (teamIds) {
137+
const conn = await db()
138+
return conn('moderator').whereIn('team_id', teamIds)
139+
.join('users', 'moderator.osm_id', 'users.id')
140+
.select('team_id', 'osm_id')
141+
}
142+
115143
/**
116144
* List all the teams which osm id is a moderator of.
117145
*
@@ -134,7 +162,7 @@ async function listModeratedBy (osmId) {
134162
*
135163
* @param {object} data - params for a team
136164
* @param {string} data.name - name of the team
137-
* @param {geojson} data.location - lat/lon of team
165+
* @param {geojson?} data.location - lat/lon of team
138166
* @param {int} osmId - id of first moderator
139167
* @param {object=} trx - optional parameter for database connection to re-use connection in case of nested
140168
* transactions. This is used when a team is created as part of an organization
@@ -349,6 +377,8 @@ async function associatedOrg (teamId) {
349377
module.exports = {
350378
get,
351379
list,
380+
listMembers,
381+
listModerators,
352382
listModeratedBy,
353383
create,
354384
update,

app/manage/organizations.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const organization = require('../lib/organization')
22
const team = require('../lib/team')
3+
const { teamsMembersModeratorsHelper } = require('./utils')
34

45
/**
56
* Create an organization
@@ -200,7 +201,8 @@ async function getOrgTeams (req, reply) {
200201
const { id } = req.params
201202
try {
202203
const data = await team.list({ organizationId: id })
203-
reply.send(data)
204+
const enhancedData = await teamsMembersModeratorsHelper(data)
205+
reply.send(enhancedData)
204206
} catch (err) {
205207
console.log(err)
206208
return reply.boom.badRequest(err.message)

app/manage/teams.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
const team = require('../lib/team')
22
const { prop, map } = require('ramda')
33
const urlRegex = require('url-regex')
4+
const { teamsMembersModeratorsHelper } = require('./utils')
45

56
const isUrl = urlRegex({ exact: true })
7+
const getOsmId = prop('osm_id')
68

79
async function listTeams (req, reply) {
810
const { osmId, bbox } = req.query
@@ -16,7 +18,8 @@ async function listTeams (req, reply) {
1618

1719
try {
1820
const data = await team.list({ osmId, bbox: bounds })
19-
reply.send(data)
21+
const enhancedData = await teamsMembersModeratorsHelper(data)
22+
reply.send(enhancedData)
2023
} catch (err) {
2124
console.log(err)
2225
return reply.boom.badRequest(err.message)
@@ -48,7 +51,7 @@ async function getTeam (req, reply) {
4851

4952
try {
5053
const teamData = await team.get(id)
51-
const memberIds = map(prop('osm_id'), (await team.getMembers(id)))
54+
const memberIds = map(getOsmId, (await team.getMembers(id)))
5255
const members = await team.resolveMemberNames(memberIds)
5356
const moderators = await team.getModerators(id)
5457

app/manage/utils.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const team = require('../lib/team')
2+
const { prop } = require('ramda')
3+
4+
const getId = prop('id')
5+
const getOsmId = prop('osm_id')
6+
7+
/**
8+
* Helper function for teams and organizations. Enhances teamsData with list of
9+
* moderator ids and list of member ids.
10+
*
11+
* @param {Object[]} teamsData
12+
* @returns {Promise<*>}
13+
* @async
14+
*/
15+
async function teamsMembersModeratorsHelper (teamsData) {
16+
const teamIds = teamsData.map(getId)
17+
const [members, moderators] = await Promise.all([
18+
team.listMembers(teamIds),
19+
team.listModerators(teamIds)
20+
])
21+
return teamsData.map(team => {
22+
const predicate = ({ team_id }) => team_id === team.id
23+
return {
24+
...team,
25+
members: members.filter(predicate).map(getOsmId),
26+
moderators: moderators.filter(predicate).map(getOsmId)
27+
}
28+
})
29+
}
30+
31+
module.exports = {
32+
teamsMembersModeratorsHelper
33+
}

app/tests/api/organization-api.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,10 @@ test('get org teams', async t => {
209209

210210
const orgTeams = await agent.get(`/api/organizations/${res.body.id}/teams`)
211211
t.is(orgTeams.body.length, 2)
212+
orgTeams.body.forEach(item => {
213+
t.truthy(item.name)
214+
t.truthy(item.id)
215+
t.truthy(item.members.length)
216+
t.truthy(item.moderators.length)
217+
})
212218
})

app/tests/api/team-api.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ test('get team list', async t => {
124124
teams.body.forEach(item => {
125125
t.truthy(item.name)
126126
t.truthy(item.id)
127+
t.truthy(item.members.length)
128+
t.truthy(item.moderators.length)
127129
})
128130
})
129131

app/tests/api/team-model.test.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const path = require('path')
22
const test = require('ava')
33
const db = require('../../db')
44
const team = require('../../lib/team')
5+
const { prop } = require('ramda')
56

67
const migrationsDirectory = path.join(__dirname, '..', '..', 'db', 'migrations')
78

@@ -57,6 +58,32 @@ test('list teams', async (t) => {
5758
})
5859
})
5960

61+
const getOsmId = prop('osm_id')
62+
63+
test('list team(s) members', async (t) => {
64+
const team1 = await team.create({ name: 'list team members test' }, 1)
65+
const team2 = await team.create({ name: 'list team members test2' }, 1)
66+
await team.addMember(team2.id, 2)
67+
const list = await team.listMembers([team1.id, team2.id])
68+
const team1Members = list.filter(({ team_id }) => team_id === team1.id).map(getOsmId)
69+
const team2Members = list.filter(({ team_id }) => team_id === team2.id).map(getOsmId)
70+
t.deepEqual(team1Members, [1])
71+
t.deepEqual(team2Members, [1, 2])
72+
})
73+
74+
test('list team(s) moderators', async (t) => {
75+
const team1 = await team.create({ name: 'list team mods test' }, 1)
76+
const team2 = await team.create({ name: 'list team mods test2' }, 1)
77+
await team.addMember(team2.id, 2)
78+
await team.assignModerator(team2.id, 2)
79+
await team.removeModerator(team2.id, 1)
80+
const list = await team.listModerators([team1.id, team2.id])
81+
const team1Mods = list.filter(({ team_id }) => team_id === team1.id).map(getOsmId)
82+
const team2Mods = list.filter(({ team_id }) => team_id === team2.id).map(getOsmId)
83+
t.deepEqual(team1Mods, [1])
84+
t.deepEqual(team2Mods, [2])
85+
})
86+
6087
test('update a team', async (t) => {
6188
const data = await team.create({ name: 'poi team 1' }, 1)
6289
const updated = await team.update(data.id, { name: 'road team 1' })

docs/api.yml

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ components:
5555
type: string
5656
format: uri
5757
description: link to organized editing policy of the team
58+
members:
59+
type: array
60+
description: list of team member's osm id
61+
items:
62+
type: number
63+
moderators:
64+
type: array
65+
description: list of team member's osm id
66+
items:
67+
type: number
5868
TeamList:
5969
type: array
6070
items:
@@ -503,7 +513,7 @@ paths:
503513
moderator may exist concurrently. Moderators are listed in the
504514
TeamModeratorList schema.
505515
tags:
506-
- teams
516+
- teams
507517
responses:
508518
'200':
509519
description: member was promoted to moderator
@@ -522,7 +532,7 @@ paths:
522532
Remove/Demote a moderator of a team. At least one moderator must exist
523533
for a team. Moderators are listed in the TeamModeratorList schema.
524534
tags:
525-
- teams
535+
- teams
526536
responses:
527537
'200':
528538
description: member was demoted from moderator
@@ -615,7 +625,7 @@ paths:
615625
Assign/Promote a user to be an owner of an organization. More than one
616626
owner may exist concurrently. Owners can manage organizations of an organization.
617627
tags:
618-
- organizations
628+
- organizations
619629
responses:
620630
'200':
621631
description: user is promoted to owner of organization
@@ -634,7 +644,7 @@ paths:
634644
Remove/Demote an owner of an organization. At least one owner
635645
must remain in the organization.
636646
tags:
637-
- organizations
647+
- organizations
638648
responses:
639649
'200':
640650
description: user is demoted from owner
@@ -654,7 +664,7 @@ paths:
654664
manager may exist concurrently. Managers can create organizations for an organization
655665
but cannot update the organization.
656666
tags:
657-
- organizations
667+
- organizations
658668
responses:
659669
'200':
660670
description: user is promoted to manager of organization
@@ -672,7 +682,7 @@ paths:
672682
summary: >
673683
Remove/Demote manager of an organization. An org can have no managers.
674684
tags:
675-
- organizations
685+
- organizations
676686
responses:
677687
'200':
678688
description: user is demoted from manager of organization

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
]
3030
},
3131
"dependencies": {
32-
"ava": "^1.4.1",
3332
"body-parser": "^1.18.3",
3433
"chance": "^1.0.18",
3534
"compression": "^1.7.3",
@@ -83,6 +82,7 @@
8382
"@babel/core": "^7.5.5",
8483
"@babel/preset-env": "^7.5.5",
8584
"@babel/preset-react": "^7.0.0",
85+
"ava": "^3.7.0",
8686
"babelify": "^10.0.0",
8787
"budo": "^11.6.3",
8888
"devseed-standard": "^1.1.0",

0 commit comments

Comments
 (0)